How to get current category in magento2? - magento2

How can i get current category in magento2 ?
I want to get category name and category id in custom phtml file.

The above to seem correct, but I think that jumping straight to the Registry is not the best approach. Magento provides a Layer Resolver that already encapsulates that functionality. (See the TopMenu Block in the Catalog Plugins)
I suggest injecting the \Magento\Catalog\Model\Layer\Resolver class and using that to get the current category. Here is the code :
<?php
namespace FooBar\Demo\Block;
class Demo extends \Magento\Framework\View\Element\Template
{
private $layerResolver;
public function __construct(
\Magento\Framework\View\Element\Template\Context $context,
\Magento\Catalog\Model\Layer\Resolver $layerResolver,
array $data = []
) {
parent::__construct($context, $data);
$this->layerResolver = $layerResolver;
}
public function getCurrentCategory()
{
return $this->layerResolver->get()->getCurrentCategory();
}
}
Here is what the actual getCurrentCategory() method does in the Resolver Class.
public function getCurrentCategory()
{
$category = $this->getData('current_category');
if ($category === null) {
$category = $this->registry->registry('current_category');
if ($category) {
$this->setData('current_category', $category);
} else {
$category = $this->categoryRepository->get($this->getCurrentStore()->getRootCategoryId());
$this->setData('current_category', $category);
}
}
return $category;
}
As you can see, it does still use the registry but it provides a fallback in case that fails.

Magento sets registry for categories being accessed. So, to get currenct category, use following method:
/**
* #param \Magento\Framework\Registry $registry
*/
protected $_registry;
public function __construct(
\Magento\Framework\Registry $registry
) {
$this->_registry = $registry;
}
and then use:
$category = $this->_registry->registry('current_category');//get current category
Now you can access the collection and fetch details such as $category->getName()

No need to use the object manager or inject class. You can use a built-in helper class Magento\Catalog\Helper\Data in the following way.
<?php
$catalogHelperData = $this->helper('Magento\Catalog\Helper\Data');
$categoryObject = $catalogHelperData->getCategory();
$categoryId = $categoryObject->getId();
$categoryName = $categoryObject->getName();
?>
This code snippet should work for any phtml (built-in or custom) file which is related to product listing page or product detail page.

Try this code. this will definitely help you.
<?php
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$category = $objectManager->get('Magento\Framework\Registry')->registry('current_category');//get current category
echo $category->getId();
echo $category->getName();
?>

In *.phtml files at Category page can get Category data with following snippet:
$currentCategory = $this->helper('Magento\Catalog\Helper\Data')->getCategory();

Related

In Magento 2, How can i get current product id via custom module at admin panel

In Magento 2.2.5, how can i get current product id at admin panel ?
Actually i have created a module at product edit page admin panel, There i wants to get current product id like frontend we use $this->_registry->registry('current_product');
Please suggest me correct script.
You can basically do it the same way as you already do on frontend. In your Block Class, include registry and then use it in your method, like this:
namespace Vendor\Module\Block\Adminhtml\...;
class Dummy
{
protected $_coreRegistry = null;
public function __construct(
...
\Magento\Framework\Registry $registry,
...
)
{
...
$this->_registry = $registry;
...
}
public function dummyfunction()
{
$_product_id = $this->_registry->registry('product'))->getId();
}
}
Alternatively use object manager, like this:
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$product = $objectManager->get('Magento\Framework\Registry')->registry('current_product');
$_product_id = $product->getId();

conversion of magento 1 into magento 2

I was using fetch_assoc() method in magento 1 .
I want to convert it into Magento 2 . there is no fetch_assoc() method in magento 2.
if(is_object($result))
{
while ($resultsArray =$result->fetch_assoc())
{
if(empty($data))
{
$data[] = array_keys($resultsArray);
}
$data[] = $resultsArray;
} var_dump($data);
}
I'm not sure my proposed solution is useful for you or not but the best approach to fetch data in Magento 2 is based on Models and Collections.
Step 1: Firstly, you have to create a Model file in your module
<?php
namespace <Vendor_Name>\<Module_Name>\Model;
use Magento\Framework\Model\AbstractModel;
class Data extends AbstractModel
{
protected function _construct()
{
$this->_init('<Vendor_Name>\<Module_Name>\Model\ResourceModel\Data');
}
}
Step 2: Create ResourceModel file in your custom module
<?php
namespace <Vendor_Name>\<Module_Name>\Model\ResourceModel;
use \Magento\Framework\Model\ResourceModel\Db\AbstractDb;
class Data extends AbstractDb
{
protected function _construct()
{
// Second parameter is a primary key of the table
$this->_init('Table_Name', 'id');
}
}
Step 3: Create Collection file to initialize Model and ResourceModel files.
namespace <Vendor_Name>\<Module_Name>\Model\ResourceModel\Data;
use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;
class Collection extends AbstractCollection
{
protected function _construct()
{
$this->_init(
'<Vendor_Name>\<Module_Name>\Model\Data',
'<Vendor_Name>\<Module_Name>\Model\ResourceModel\Data'
);
}
}
Step 4: Last thing that you need to do is create a Block file in the same module and utilize collection, something like this:
namespace <Vendor_Name>\<Module_Name>\Block;
use Magento\Framework\View\Element\Template\Context;
use Magento\Framework\View\Element\Template;
use <Vendor_Name>\<Module_Name>\Model\Data as DataCollection;
class Custom_Module extends Template
{
protected $dataCollection;
public function __construct(Context $context, DataCollection $dataCollection)
{
$this->_dataCollection = $dataCollection;
parent::__construct($context);
}
public function getDataCollecton()
{
$collection = $this->_dataCollection->getCollection();
return $collection;
}
}
Another Solution
You can also use fetchAll instead of fetch_assoc() in Magento 2, if you don't want to implement models and collections based solution, something like this:
// Select Data from table
$sql = "Select * FROM " . $tableName;
$result = $connection->fetchAll($sql);
and for reference, you can also have a look into Magento2 – Write Custom Mysql Query (Without Using Model)
I think we can use something like below :
$adapter = $this->resourceConnection->getConnection($resource);
$stmt = $adapter->query($sql);
// Use FETCH_NUM so we are not dependent on the CASE attribute of the PDO connection
$results = $stmt->fetchAll(\Zend_Db::FETCH_ASSOC);
Or if we have $connection instanceof \Magento\Framework\DB\Adapter\AdapterInterface
$connection->fetchAll($sql, $binds, \PDO::FETCH_ASSOC);
By using those, i think you're gonna get the same result to magento 1 fetch_assoc
Alternative of the fetch_assoc() in magento 2 is fetchAssoc($SQL_QUERY)
Below is the example.
For get order where status is pending data using fetchAssoc(SQL_QUERY)
<?php
namespace Path\To\Class;
use Magento\Framework\App\ResourceConnection;
class fetchAssoc {
const ORDER_TABLE = 'sales_order';
/**
* #var ResourceConnection
*/
private $resourceConnection;
public function __construct(
ResourceConnection $resourceConnection
) {
$this->resourceConnection = $resourceConnection;
}
/**
* fetchAssoc Sql Query
*
* #return string[]
*/
public function fetchAssocQuery()
{
$connection = $this->resourceConnection->getConnection();
$tableName = $connection->getTableName(self::ORDER_TABLE);
$query = $connection->select()
->from($tableName,['entity_id','status','grand_total'])
->where('status = ?', 'pending');
$fetchData = $connection->fetchAssoc($query);
return $fetchData;
}
}
In magento 2 you can use same but for that you need to create database connection.
I suggest you to use resource or collection models to get the result and if you want to get the first row in object format then you should use
getFirstItem();
I think Magento 2 has support for this in class \Magento\Framework\DB\Adapter\AdapterInterface. You can create an instance for AdapterInterface by dependency injection or directly by object manager.
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
/** #var \Magento\Framework\App\ResourceConnection $resourceConnection */
$resourceConnection = $objectManager->get(\Magento\Framework\App\ResourceConnection::class);
/** #var \Magento\Framework\DB\Adapter\AdapterInterface $connection */
$connection = $resourceConnection->getConnection(\Magento\Framework\App\ResourceConnection::DEFAULT_CONNECTION);
$sql = "YOUR SELECT QUERY HERE";
$data = $connection->fetchAssoc($sql);

where is the basepath variable being set for the zend basePath view helper (maybe a factory)

Id like to see how the base path view helper sets up the base path variable inside the helper class.
This is an internal based questions, as I imagine its being done with a factory behind the scenes.
I needed to replicate it with a custom version but im hardcoding the base path currently: You'll see that even though its extending the basepath viewhelper i cannot configure the basepath variable without this current solution of hardcoding it
class PlutoBasePath extends \Zend\View\Helper\BasePath
{
public function __construct()
{
/**
* #todo
* #var Ambiguous $basePath
*/
$this->basePath = Pluto::registry('prepend_location_url');
}
public function __invoke($file = null)
{
if (null === $this->basePath) {
throw new Exception\RuntimeException('No base path provided');
}
if (null !== $file) {
\Pluto\Stdlib\FilesystemUtils::sanitizeFilePaths($file);
\Pluto\Stdlib\FilesystemUtils::trimLeadingPath($file);
}
return $this->basePath.$file;
}
}
Id rather use a factory but I dont know how to access the base path set logic WHICH SETS UP THE FACTORY BASE PATH FOR THE base path view helper to setup the custom base path correctly
How is it possible for me to see the factory creation of the base path view helper is my base question
I seem to have found where the basepath is set, here Zend\Mvc\Service\ViewHelperManagerFactory::createBasePathHelperFactory().
private function createBasePathHelperFactory(ContainerInterface $services)
{
return function () use ($services) {
$config = $services->has('config') ? $services->get('config') : [];
$helper = new ViewHelper\BasePath;
if (Console::isConsole()
&& isset($config['view_manager']['base_path_console'])
) {
$helper->setBasePath($config['view_manager']['base_path_console']);
return $helper;
}
if (isset($config['view_manager']) && isset($config['view_manager']['base_path'])) {
$helper->setBasePath($config['view_manager']['base_path']);
return $helper;
}
$request = $services->get('Request');
if (is_callable([$request, 'getBasePath'])) {
$helper->setBasePath($request->getBasePath());
}
return $helper;
};
}
I hope this helps
Here is a piece of code that I use in my application and that should answer your question (to be adapted according to the schema of your project)
use Zend\View\Renderer\PhpRenderer;
use Zend\View\Resolver;
...
$stack = new Resolver\TemplatePathStack(
[
'script_paths' => [
__DIR__ . '/../../../view'
]
]);
$resolver = new Resolver\AggregateResolver();
$resolver->attach($stack);
$renderer = new PhpRenderer();
$renderer->setResolver($resolver)
->plugin('basePath')
->setBasePath('/');

Symfony serializer - set circular reference global

Is there any way to set the circular reference limit in the serializer component of Symfony (not JMSSerializer) with any config or something like that?
I have a REST Application with FOSRestBundle and some Entities that contain other entities which should be serialized too. But I'm running into circular reference errors.
I know how to set it like this:
$encoder = new JsonEncoder();
$normalizer = new ObjectNormalizer();
$normalizer->setCircularReferenceHandler(function ($object) {
return $object->getName();
});
But this has to be done in more than one controller (overhead for me).
I want to set it globally in the config (.yml) e.g. like this:
framework:
serializer:
enabled: true
circular_limit: 5
Found no serializer API reference for this so I wonder is it possible or not?
For a week have I been reading Symfony source and trying some tricks to get it work (on my project and without installing a third party bundle: not for that functionality) and I finally got one. I used CompilerPass (https://symfony.com/doc/current/service_container/compiler_passes.html)... Which works in three steps:
1. Define build method in bundle
I choosed AppBundle because it is my first bundle to load in app/AppKernel.php.
src/AppBundle/AppBundle.php
<?php
namespace AppBundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class AppBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new AppCompilerPass());
}
}
2. Write your custom CompilerPass
Symfony serializers are all under the serializer service. So I just fetched it and added to it a configurator option, in order to catch its instanciation.
src/AppBundle/AppCompilerPass.php
<?php
namespace AppBundle;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
class AppCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$container
->getDefinition('serializer')
->setConfigurator([
new Reference(AppConfigurer::class), 'configureNormalizer'
]);
}
}
3. Write your configurer...
Here, you create a class following what you wrote in the custom CompilerPass (I choosed AppConfigurer)... A class with an instance method named after what you choosed in the custom compiler pass (I choosed configureNormalizer).
This method will be called when the symfony internal serializer will be created.
The symfony serializer contains normalizers and decoders and such things as private/protected properties. That is why I used PHP's \Closure::bind method to scope the symfony serializer as $this into my lambda-like function (PHP Closure).
Then a loop through the nomalizers ($this->normalizers) help customize their behaviours. Actually, not all of those nomalizers need circular reference handlers (like DateTimeNormalizer): the reason of the condition there.
src/AppBundle/AppConfigurer.php
<?php
namespace AppBundle;
class AppConfigurer
{
public function configureNormalizer($normalizer)
{
\Closure::bind(function () use (&$normalizer)
{
foreach ($this->normalizers as $normalizer)
if (method_exists($normalizer, 'setCircularReferenceHandler'))
$normalizer->setCircularReferenceHandler(function ($object)
{
return $object->getId();
});
}, $normalizer, $normalizer)();
}
}
Conclusion
As said earlier, I did it for my project since I dind't wanted FOSRestBundle nor any third party bundle as I've seen over Internet as a solution: not for that part (may be for security). My controllers now stand as...
<?php
namespace StoreBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class ProductController extends Controller
{
/**
*
* #Route("/products")
*
*/
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$data = $em->getRepository('StoreBundle:Product')->findAll();
return $this->json(['data' => $data]);
}
/**
*
* #Route("/product")
* #Method("POST")
*
*/
public function newAction()
{
throw new \Exception('Method not yet implemented');
}
/**
*
* #Route("/product/{id}")
*
*/
public function showAction($id)
{
$em = $this->getDoctrine()->getManager();
$data = $em->getRepository('StoreBundle:Product')->findById($id);
return $this->json(['data' => $data]);
}
/**
*
* #Route("/product/{id}/update")
* #Method("PUT")
*
*/
public function updateAction($id)
{
throw new \Exception('Method not yet implemented');
}
/**
*
* #Route("/product/{id}/delete")
* #Method("DELETE")
*
*/
public function deleteAction($id)
{
throw new \Exception('Method not yet implemented');
}
}
The only way I've found is to create your own object normalizer to add the circular reference handler.
A minimal working one can be:
<?php
namespace AppBundle\Serializer\Normalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
class AppObjectNormalizer extends ObjectNormalizer
{
public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null, PropertyAccessorInterface $propertyAccessor = null, PropertyTypeExtractorInterface $propertyTypeExtractor = null)
{
parent::__construct($classMetadataFactory, $nameConverter, $propertyAccessor, $propertyTypeExtractor);
$this->setCircularReferenceHandler(function ($object) {
return $object->getName();
});
}
}
Then declare as a service with a slithly higher priority than the default one (which is -1000):
<service
id="app.serializer.normalizer.object"
class="AppBundle\Serializer\Normalizer\AppObjectNormalizer"
public="false"
parent="serializer.normalizer.object">
<tag name="serializer.normalizer" priority="-500" />
</service>
This normalizer will be used by default everywhere in your project.

Zend accessing user data in all controllers

Searched and searched for some guidance but cant seem to get anything to work. I wont to access the user data in any index controller to save repetetive code. I have created a ACTION HELPER which calls the Session and returns the users data. Below is how I implemented it all. Calling the helper works but I cant get the data out in any controller.
Application.ini
autoloaderNamespaces[] = "ZC"
Bootstrap.php
protected function _initActionHelpers()
{
Zend_controller_Action_HelperBroker::addHelper(new ZC_Action_Helpers_User());
}
User.php
<?php
Class ZC_Action_Helpers_User extends Zend_Controller_Action_Helper_Abstract
{
public function direct()
{
$storage = new Zend_Auth_Storage_Session();
$data = $storage->read();
$this->_user = $data;
}
}
IndexController.php
<?php
class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
$this->_helper->user;
}
I have no problems with the code but say for example how would I get the USERNAME or USERID from the helper???
Thanks for taking the time in looking at this.
J
First, your plugin function have to return the user information and not set it as a class variable:
Class ZC_Action_Helpers_User extends Zend_Controller_Action_Helper_Abstract
{
public function direct()
{
$storage = new Zend_Auth_Storage_Session();
$data = $storage->read();
return $data;
}
}
Then you can use the plugin in any controller you need:
$user = $this->_helper->User->Direct()
You might want to rename some of these functions and classes.
Try to declared in bootstrap path to helpers...
Zend_Controller_Action_HelperBroker::addPath(
APPLICATION_PATH .'/controllers/helpers');
Next use google for this simply question.... howto use helper in ZF