I'm new to ZF2. After few days of trying to figure out how all this stuff should work I was unable to figure out how should I call TableGateway Model from Service.
So I have Controller:
class SubscriberController extends AbstractActionController
{
/**
* #var \Subscriber\Service\SubscriberServiceInterface
*/
private $subscriberService;
/**
* #param $subscriberService
*/
public function __construct(SubscriberServiceInterface $subscriberService)
{
$this->subscriberService = $subscriberService;
}
Factroy for this Controller:
class SubscriberControllerFactory implements FactoryInterface
{
/**
* Returns ArchiveController instance.
*
* #param ServiceLocatorInterface $serviceLocator
* #return SubscriberController
* #override
**/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$sm = $serviceLocator->getServiceLocator();
return new SubscriberController(
$sm->get('Subscriber\Service\SubscriberServiceInterface')
);
}
Some SubscriberTable:
class SubscriberTable
{
protected $tableGateway;
public function __construct(TableGateway $tableGateway)
{
$this->tableGateway = $tableGateway;
}
public function fetchAll()
{
$resultSet = $this->tableGateway->select();
return $resultSet;
}
And Service in which I want to get SubscriberTable instance and make some logic. But I can't figure out how should I call this instance in SubscriberService and set the DbAdapter for SubscriberTable
First implement servicelocator interface and define get and set locator functions to your service like this.
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class Yourservice implements ServiceLocatorAwareInterface{
function test(){
$this->getSubscriberTable->fetchAll(); // call to subscriber table functions
}
/**
* #table gateway Call
**/
public function getSubscriberTable()
{
if (!$this->SubscriberTable) {
$sm = $this->getServiceLocator();
$this->SubscriberTable = $sm->get('Application\Model\SubscriberTable');
}
return $this->SubscriberTable;
}
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->serviceLocator = $serviceLocator;
}
public function getServiceLocator()
{
return $this->serviceLocator;
}
}
Hope it will help you.
Related
Example:
I have 2 observer classes.
1- category.php
2- product.php
I want to store the category.php data in a session variable, like $_SESSION['category']= $data; and call that session in product.php class.
Like this:
<?php
namespace Vendor\Module\Observer;
use Magento\Framework\Event\Observer as EventObserver;
use Magento\Framework\Event\ObserverInterface;
use Magento\Checkout\Model\Session as CheckoutSession;
class MyOberver implements ObserverInterface
{
protected $checkoutSession;
public function __construct(
CheckoutSession $checkoutSession
) {
$this->_checkoutSession = $checkoutSession;
}
/**
* #param EventObserver $observer
* #return $this
*/
public function execute(EventObserver $observer)
{
$setValue = $this->_checkoutSession->setVar('dit is een sessie test hoppa');
$getValue = $this->_checkoutSession->getVar();
return $this;
}
}
I recently decided to use Zend Framework 3 after 3 years of using Zend Framework 1. This decision has given me headaches, Zend 3 instead of making things easier made things more difficult.
In Zend 1, I customize the url for the selected template in the database as follows:
public function getUrl(string $file = '')
{
if($this->_helperBaseUrl === null) {
$this->_helperBaseUrl = new Zend_View_Helper_BaseUrl();
}
return $this->_helperBaseUrl->baseUrl($file);
}
public function getSkinUrl(string $file = '')
{
$themePath = 'themes/my-theme/'; //get from database
return $this->getUrl($themePath . ltrim($file, '/\\'));
}
Then in any part of the application (models, helpers, plugins and views) I can access this function like this:
//view/scripts/index/index.phtml
$url_logo = My::app()->getSkinUrl('logo.jpg');
//this return http://example.com/themes/my-theme/logo.jpg
In Zend 3 it has been very difficult for me. Does anyone know of any way to do it in Zend 3? Or How to get the baseUrl from a model in Zend 3?
In Zend Framework 2/3 you can inject almost any class into another. For example if you need basePath plugin (which is available in view context) you can inject this plugin into your model/service or controller class. This is the recommended way:
This is class where you need this plugin or any other service
use Zend\View\Helper\BasePath;
class MyService
{
/**
* #var BasePath
*/
protected $plugin;
/**
* MyService constructor.
*
* #param BasePath $basePath
*/
public function __construct(BasePath $basePath)
{
$this->plugin = $basePath;
}
/**
* #return BasePath
*/
public function getPlugin()
{
return $this->plugin;
}
/**
* #param BasePath $plugin
*/
public function setPlugin($plugin)
{
$this->plugin = $plugin;
}
}
Now, you need to factory to inject one dependency into another
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use MyNamespace\Service\MyService;
class MyServiceFactory implements FactoryInterface
{
/**
*
* #param ContainerInterface $container
* #param string $requestedName
* #param null|array $options
* #return MyService
*/
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$class = $requestedName ? $requestedName : MyService::class;
$plugin = $container->get('ViewHelperManager')->get('BasePath'); // inject this class
$myService = new $class($plugin); // into this class
return $myService;
}
/**
* Provided for backwards compatibility; proxies to __invoke().
*
* #param ContainerInterface|ServiceLocatorInterface $container
* #return MyService
*/
public function createService(ServiceLocatorInterface $container)
{
return $this($container, MyService::class);
}
}
Ok, now MyService has basePath plugin, but to use it in controller you have to inject your service into controller. So...
IndexController
use MyNamespace\Service\MyService;
use Zend\Mvc\Controller\AbstractActionController;
class IndexController extends AbstractActionController
{
/**
* #var MyService
*/
protected $service;
/**
* IndexController constructor.
*
* #param MyService $service
*/
public function __construct(MyService $service)
{
$this->service = $service;
}
public function indexAction()
{
$plugin = $this->service->getPlugin(); // Zend\View\Helper\BasePath object
//...
}
}
... and factory for our controller...
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use MyNamespace\Controller\IndexController;
class IndexControllerFactory implements FactoryInterface
{
/**
*
* #param ContainerInterface $container
* #param string $requestedName
* #param null|array $options
* #return IndexController
*/
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$class = $requestedName ? $requestedName : IndexController::class;
$myService = $container->getServiceLocator()->get('MyNamespace\Service\MyService');
$controller = new $class($myService);
return $controller;
}
/**
* Provided for backwards compatibility; proxies to __invoke().
*
* #param ContainerInterface|ServiceLocatorInterface $container
* #return IndexController
*/
public function createService(ServiceLocatorInterface $container)
{
return $this($container, IndexController::class);
}
}
It's almost done. Last step is to set configuration in module.config.php file
use MyNamespace\Controller;
use MyNamespace\Factory;
return [
//...
'service_manager' => [
'factories' => [
Service\MyService::class => Factory\Service\MyServiceFactory::class
]
],
'controllers' => [
'factories' => [
Controller\IndexController::class => Factory\Controller\IndexControllerFactory::class
],
],
]
Easy, isn't it?
If you need plugin in controller, but not in your model/service class, you can skip MyService part of this "tutorial" and inject plugin directly into controller class
I'm using FOSRestBundle in a Symfony project. When it I try to handle a view, it fails during the serialization of my data with the Symfony serializer as well as with the JMSSerializer.
This is the method rendering the response:
DefaultController.php
$em = $this->getDoctrine()->getManager('magellan');
$qb = $em->createQueryBuilder();
$query = $qb->select('h')
->from('DataBundle:Holding', 'h')
->where($qb->expr()->eq('h.id', ':holding_id'))
->setParameter('holding_id', $holding_id)
->getQuery();
$results = $query->getResult();
$view = $this->view($results, 200);
// Everything's ok up to this point
return $this->handleview($view);
And these are my entities:
Holding.php
class Holding
{
...
/**
* #ORM\OneToMany(targetEntity="Subsidiary", mappedBy="holding")
*/
private $subsidiaries;
}
Subsidiary.php
class Subsidiary
{
...
/**
* #ORM\ManyToOne(targetEntity="Holding", inversedBy="subsidiaries")
* #ORM\JoinColumn(name="id_holding", referencedColumnName="id_holding")
*/
private $holding;
/**
* #ORM\OneToMany(targetEntity="Brand", mappedBy="subsidiary")
*/
private $brands;
}
Brand.php
class Brand
{
...
/**
* #ORM\ManyToOne(targetEntity="Subsidiary", inversedBy="brands")
* #ORM\JoinColumn(name="id_subsidiary", referencedColumnName="id_subsidiary")
*/
private $subsidiary;
/**
* #ORM\OneToMany(targetEntity="Product", mappedBy="brand")
*/
private $products;
}
Product.php
class Product
{
...
/**
* #ORM\ManyToOne(targetEntity="Brand", inversedBy="products")
* #ORM\JoinColumn(name="id_brand", referencedColumnName="id_brand")
*/
private $brand;
/**
* #ORM\ManyToOne(targetEntity="Sector", inversedBy="products")
* #ORM\JoinColumn(name="id_sector", referencedColumnName="id_sector")
*/
private $sector;
/**
* #ORM\OneToMany(targetEntity="Commercial", mappedBy="product")
*/
private $commercials;
}
Commercial.php
class Commercial
{
...
/**
* #ORM\ManyToOne(targetEntity="Product", inversedBy="commercials")
* #ORM\JoinColumn(name="id_product", referencedColumnName="id_product")
*/
private $product;
/**
* #ORM\OneToMany(targetEntity="CommercialReport", mappedBy="commercial")
*/
private $reports;
CommercialReport.php
class CommercialReport
{
...
/**
* #ORM\ManyToOne(targetEntity="Commercial", inversedBy="reports")
* #ORM\JoinColumn(name="id_commercial", referencedColumnName="id_commercial")
*/
private $commercial;
}
Sector.php
class Sector
{
...
/**
* #ORM\OneToMany(targetEntity="Product", mappedBy="sector")
*/
private $products;
}
When using the default symfony serializer, I get the following error:
"message":"A circular reference has been detected (configured limit:
1).","class":"Symfony\Component\Serializer\Exception\CircularReferenceException"
And when using the JMSSerializer, when I go to the corresponding page of the controller, the page just never finishes loading. At the same time in the dev.log file new Doctrine.debug entries with requests to my DB are added every second.
$normalizers->setCircularReferenceHandler(function ($object) {
return $object->getId();
});
Just add it after you make the instance if your objectNormalizer()
it worl perfectly for me
If you use FosRestBundle, you can use the GROUPS for the serializer. There is an annotation given by FosRestBundle : #FOS\RestBundle\Controller\Annotations\View(serializerGroups={"user"})
Your group can exclude the circular property.
Another idea you can do this. In your app/config/services.yml
circular_reference_handler:
public: false
class: callback
factory: [AppBundle\Serializer\CircularHandlerFactory, getId]
serializer.normalizer.object:
class: Symfony\Component\Serializer\Normalizer\ObjectNormalizer
arguments: ["#serializer.mapping.class_metadata_factory", null, "#serializer.property_accessor"]
public: false
tags: [serializer.normalizer]
calls:
- method: setCircularReferenceLimit
arguments: [1]
- method: setCircularReferenceHandler
arguments: ["#circular_reference_handler"]
The factory can be like this:
namespace AppBundle\Serializer;
class CircularHandlerFactory
{
/**
* #return \Closure
*/
public static function getId()
{
return function ($object) {
return $object->getId();
};
}
}
I'm trying to use a m:n relation, the same way as FrontEndUser is related to FrontEndUserGroup, e.g. without intermediate mm table. In my controller, I build my object, then I call $barRepository->update($barObject); to update the values of my object. However, it fails on the update function with the error:
Fatal error: Call to undefined method Cbrunet\Up\Domain\Model\Foo::getPosition() in /home/cbrunet/websites/typo3_src-6.1.1/typo3/sysext/extbase/Classes/Persistence/Generic/Backend.php on line 486
where Foo is the type of the object contained in the ObjectStorage of Bar. My understanding is that getPosition should be called on the ObjectStorage, not on the object contained into that ObjectStorage. However, I cannot figure out why this is not working in my case.
This is in TYPO3 6.1.5. Any hint would be appreciated.
The model of Bar which has a m:n relation to Foo looks like:
namespace Cbrunet\Up\Domain\Model;
class Bar extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity {
/**
* #var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Cbrunet\Up\Domain\Model\Foo>
*/
protected $myprop;
public function __construct() {
$this->myprop = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
}
/**
* #param \TYPO3\CMS\Extbase\Persistence\ObjectStorage $myprop
* #return void
*/
public function setMyprop(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $myprop) {
$this->myprop = $myprop;
}
/**
* #param \Cbrunet\Up\Domain\Model\Foo $myprop
* #return void
*/
public function addMyprop(\Cbrunet\Up\Domain\Model\Foo $myprop) {
$this->myprop->attach($myprop);
}
/**
* #param \Cbrunet\Up\Domain\Model\Foo $myprop
* #return void
*/
public function removeMyprop(\Cbrunet\Up\Domain\Model\Foo $myprop) {
$this->myprop->detach($myprop);
}
/**
* #return \TYPO3\CMS\Extbase\Persistence\ObjectStorage
*/
public function getMyprop() {
return $this->myprop;
}
}
The relevant code in my controller looks like:
/**
* action update
*
* #return void
*/
public function updateAction() {
$args = $this->request->getArgument('myargs');
foreach ($args as $k=>$val) {
$pp = $this->barRepository->findOneByAprop($k); // another prop of Bar, not illustrated in the code above.
$listepour = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
foreach ($val as $p) {
$ap = $this->fooRepository->findOneByUid(intval($p));
$listepour->attach($ap);
}
$pp->setMyprop($listepour);
$this->barRepository->update($pp); // error occurs here
}
$this->redirect('list');
}
Do you also have configured your TCA?
do you have an initStorageObjects-function in your domain model?
Also you can try to build these case with the extension-manager and compare the code.
How do I inject the service manager into a Doctrine repository to allow me to retrieve the Doctrine Entity Manager?
I using the ZF2-Commons DoctrineORMModule and are trying to implement the repository example listed in the Doctrine Tutorial (bottom of tutorial in link below):
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/getting-started.html
However, I keep getting a message "Fatal error: Call to a member function get() on a non-object in C:\zendProject\zf2 ... ", which suggests that I do not have a working instance of the service locator.
My Doctrine repository looks like this:
namespace Calendar\Repository;
use Doctrine\ORM\EntityRepository,
Calendar\Entity\Appointment,
Calendar\Entity\Diary;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class ApptRepository extends EntityRepository implements ServiceLocatorAwareInterface
{
protected $services;
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->services = $serviceLocator;
}
public function getServiceLocator()
{
return $this->services;
}
public function getUserApptsByDate()
{
$dql = "SELECT a FROM Appointment a";
$em = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
$query = $em()->createQuery($dql);
return $query->getResult();
}
}
I then want to call this in my controller using the following pattern:
$diary = $em->getRepository('Calendar\Entity\Appointment')->getUserApptsByDate();
EDIT: The attached link suggests that I may need to convert the class to a service,
https://stackoverflow.com/a/13508799/1325365
However, if this is the best route, how would I then make my Doctrine Entity aware of the service. At the moment I include an annotation in the doc block pointing to the class.
#ORM\Entity (repositoryClass="Calendar\Repository\ApptRepository")
The way i approach things is this:
First i register a Service for each entity. This is done inside Module.php
public function getServiceConfig()
{
return array(
'factories' => array(
'my-service-entityname' => 'My\Factory\EntitynameServiceFactory',
)
);
}
Next thing would be to create the factory class src\My\Factory\EntitynameServiceFactory.php. This is the part where you inject the EntityManager into your Entity-Services (not into the entity itself, the entity doesn't need this dependency at all)
This class looks something like this:
<?php
namespace My\Factory;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\FactoryInterface;
use My\Service\EntitynameService;
class EntitynameServiceFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$service = new EntitynameService();
$service->setEntityManager($serviceLocator->get('Doctrine\ORM\EntityManager'));
return $service;
}
}
Next thing in line is to create the src\My\Service\EntitynameService.php. And this is actually the part where you create all the getter functions and stuff. Personally i extend these Services from a global DoctrineEntityService i will first give you the code for the EntitynameService now. All this does is to actually get the correct repository!
<?php
namespace My\Service;
class EntitynameService extends DoctrineEntityService
{
public function getEntityRepository()
{
if (null === $this->entityRepository) {
$this->setEntityRepository($this->getEntityManager()->getRepository('My\Entity\Entityname'));
}
return $this->entityRepository;
}
}
This part until here should be quite easy to understand (i hope), but that's not all too interesting yet. The magic is happening at the global DoctrineEntityService. And this is the code for that!
<?php
namespace My\Service;
use Zend\EventManager\EventManagerAwareInterface;
use Zend\EventManager\EventManagerInterface;
use Zend\ServiceManager\ServiceManagerAwareInterface;
use Zend\ServiceManager\ServiceManager;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityRepository;
class DoctrineEntityService implements
ServiceManagerAwareInterface,
EventManagerAwareInterface
{
protected $serviceManager;
protected $eventManager;
protected $entityManager;
protected $entityRepository;
/**
* Returns all Entities
*
* #return EntityRepository
*/
public function findAll()
{
$this->getEventManager()->trigger(__FUNCTION__ . '.pre', $this, array('entities' => $entities));
$entities = $this->getEntityRepository()->findAll();
$this->getEventManager()->trigger(__FUNCTION__ . '.post', $this, array('entities' => $entities));
return $entities;
}
public function find($id) {
return $this->getEntityRepository()->find($id);
}
public function findByQuery(\Closure $query)
{
$queryBuilder = $this->getEntityRepository()->createQueryBuilder('entity');
$currentQuery = call_user_func($query, $queryBuilder);
// \Zend\Debug\Debug::dump($currentQuery->getQuery());
return $currentQuery->getQuery()->getResult();
}
/**
* Persists and Entity into the Repository
*
* #param Entity $entity
* #return Entity
*/
public function persist($entity)
{
$this->getEventManager()->trigger(__FUNCTION__ . '.pre', $this, array('entity'=>$entity));
$this->getEntityManager()->persist($entity);
$this->getEntityManager()->flush();
$this->getEventManager()->trigger(__FUNCTION__ . '.post', $this, array('entity'=>$entity));
return $entity;
}
/**
* #param \Doctrine\ORM\EntityRepository $entityRepository
* #return \Haushaltportal\Service\DoctrineEntityService
*/
public function setEntityRepository(EntityRepository $entityRepository)
{
$this->entityRepository = $entityRepository;
return $this;
}
/**
* #param EntityManager $entityManager
* #return \Haushaltportal\Service\DoctrineEntityService
*/
public function setEntityManager(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
return $this;
}
/**
* #return EntityManager
*/
public function getEntityManager()
{
return $this->entityManager;
}
/**
* Inject an EventManager instance
*
* #param EventManagerInterface $eventManager
* #return \Haushaltportal\Service\DoctrineEntityService
*/
public function setEventManager(EventManagerInterface $eventManager)
{
$this->eventManager = $eventManager;
return $this;
}
/**
* Retrieve the event manager
* Lazy-loads an EventManager instance if none registered.
*
* #return EventManagerInterface
*/
public function getEventManager()
{
return $this->eventManager;
}
/**
* Set service manager
*
* #param ServiceManager $serviceManager
* #return \Haushaltportal\Service\DoctrineEntityService
*/
public function setServiceManager(ServiceManager $serviceManager)
{
$this->serviceManager = $serviceManager;
return $this;
}
/**
* Get service manager
*
* #return ServiceManager
*/
public function getServiceManager()
{
return $this->serviceManager;
}
}
So what does this do? This DoctrineEntityService pretty much is all what you globally need (to my current experience). It has the fincAll(), find($id) and the findByQuery($closure)
Your next question (hopefully) would only be "How to use this from my controller now?". It's as simple as to call your Service, that you have set up in the first step! Assume this code in your Controllers
public function someAction()
{
/** #var $entityService \my\Service\EntitynameService */
$entityService = $this->getServiceLocator()->get('my-service-entityname');
// A query that finds all stuff
$allEntities = $entityService->findAll();
// A query that finds an ID
$idEntity = $entityService->find(1);
// A query that finds entities based on a Query
$queryEntity = $entityService->findByQuery(function($queryBuilder){
/** #var $queryBuilder\Doctrine\DBAL\Query\QueryBuilder */
return $queryBuilder->orderBy('entity.somekey', 'ASC');
});
}
The function findByQuery() would expect an closure. The $queryBuilder (or however you want to name that variable, you can choose) will be an instance of \Doctrine\DBAL\Query\QueryBuilder. This will always be tied to ONE Repository though! Therefore entity.somekey the entity. will be whatever repository you are currently working with.
If you need access to the EntityManager you'd either only instantiate only the DoctrineEntityService or call the $entityService->getEntityManager() and continue from there.
I don't know if this approach is overly complex or something. When setting up a new Entity/EntityRepository, all you need to do is to add a new Factory and a new Service. Both of those are pretty much copy paste with two line change of code in each class.
I hope this has answered your question and given you some insight of how work with ZF2 can be organized.
As long as you extend the Doctrine\ORM\EntityRepository, you have immediate access to the entity manager by calling EntityRepository::getEntityManager() or the $_em attribute. The inheritence from the Doctrine\ORM\EntityRepository class allow you to do so.
Your method should now look like this:
public function getUserApptsByDate()
{
$dql = "SELECT a FROM Appointment a";
$em = $this->getEntityManager();// Or $em=$this->_em;
$query = $em()->createQuery($dql);
return $query->getResult();
}
I always keep in mind that access to my data should go from the web front (Zend MVC, Service Manager) to the persistence layer (Doctrine). My persistence (entities, repositories...) layer should not refer to the web front or neither know that it exists. If my system is doing the inverse at some level, then probably I'm doing something wrong.
Happy end of year