// in some file
/**
* #typedef myspace
* #property {Object} models
* #property {Object} services
*/
// in other files
/** #typedef myspace.models.ModelA */
class ModelA { ... }
/** #typedef myspace.models.ModelB */
class ModelB { ... }
/** #typedef myspace.services.ServiceA */
class ServiceA { ... }
How to define classes with JSDoc if I want to make them as members of the namespaces myspace.models and myspace.services?
You need using #namespace and #memberOf, I believe
Related
I have some common fields in almost all tables. So I created an AbstractEntity class where I can add fields that are in common.
/**
* #MappedSuperclass
*/
abstract class AbstractEntity
{
/**
* #ORM\Id()
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer", options={"unsigned"=true})
*/
protected int $id;
/**
* #ORM\Column(name="created_at", type="datetime", nullable=false)
*/
protected string $createdAt;
public function getId(): int
{
return $this->id;
}
public function createdAt(): string
{
return $this->createdAt;
}
public function setShopLocale(string $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}
}
Now, I extend Abstractclass in other EntityClass
class Random extends AbstractEntity
{
}
I am getting following error message when I try to insert record.
Class "App\Entity\Admin\Random" sub class of "App\Entity\Admin\AbstractEntity" is not a valid entity or mapped super class.
Can a class be put into a member group like a function? For example,
/** Some module.
*
* #defgroup Group
* #{
*/
/** #name Same utility
*
*/
/** #{*/
/** Class A.
*
*/
class A
{
};
/** Function.
*
*/
void f();
/** #}*/ // end Same utility
/** Class B.
*
*/
class B
{
};
/** #}*/ // end Group
The above code will put f() into a custom section called "Same utility", but the document for class A is still in the section "class" with class B. So is there a way to group the class A into a member group as well?
When I create a class child# that I fill its values and that I save it in DB (persistent, flush) I have no worries, all is database.
When I pick a Child# up from the database. All the attributes of the Parent1 are not hydrated, I do have those of the GrandParent, the Child# but not those of the Parent1, why?
/**
* #ORM\Entity
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="class", type="int")
* #ORM\DiscriminatorMap({
* 0 = "Parent2",
* 1 = "Child1",
* 2 = "Child2"})
*/
abstract class GrandParent { ... }
/** #ORM\Entity */
abstract class Parent1 extends GrandParent { ... }
/** #ORM\Entity */
class Parent2 extends GrandPArent { ... }
/** #ORM\Entity */
class Child1 extends Parent1 { ... }
/** #ORM\Entity */
class Child2 extends Parent1 { ... }
thanks for your help.
I'm trying create something using ZF2 and Doctrine 2. But I'm kind lost about what I want to achieve.
First I now that using $em = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager'); inside controller it will work.
But what I want it's something more elaborated, like this:
class BaseModel extends Doctrine\ORM\EntityManager
{
public function save()
{
$em->persist($this);
$em->flush();
}
}
/**
* #ORM\Entity
*/
class Customer extends BaseModel
{
// getters setters
}
class IndexController
{
public function indexAction()
{
$customer = new Customer();
$customer->setName('asd');
$customer->save();
Customer::findAll();
}
}
I have started this:
namespace Ws\Model;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorAwareTrait;
class BaseModel implements ServiceLocatorAwareInterface
{
use ServiceLocatorAwareTrait;
public function write() {
$em = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
return $em->getRepository(__CLASS__);
}
}
But without success.
I understand that this is more than one question, but is it possible help me here even passing some links that could help me in this journey?
Thank you.
Did you have a look at the Zend\ServiceManager\ServiceLocatorAwareTrait implemntation ? It just give you the getter and setter, but of course, if you never set it, you will never get it. So $this->getServiceLocator() will always return null.
You'd better use dependency injection (never inject service locator but the needed services) or maybe, as pointed by #Cerad, use Doctrine the way it was designed.
You're in luck. Just created that myself to use in my own vendor package.
You're code is messy though, I'm going to assume that the "Model" your refer to is the Entity and not a combination of Entity and Repository.
However, to the code!
Make special note of the namespaces and organize folder structure and file names accordingly!
File: repoName/src/Entity/AbstractEntity.php
<?php
namespace Company\Core;
use Doctrine\ORM\Mapping as ORM;
use Company\Core\Listener\ServiceLocatorAwareEntity;
/**
* Class AbstractEntity
* #package Company\Core\Entity
*
* #ORM\MappedSuperclass
* #ORM\HasLifecycleCallbacks
*/
abstract class AbstractEntity extends ServiceLocatorAwareEntity
{
/**
* #var int
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* #param int $id
* #return AbstractEntity
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
}
File: repoName/src/Service/AbstractEntityService.php
<?php
namespace Company\Core\Service;
use Doctrine\ORM\EntityManager;
use Company\Core\Interfaces\EntityServiceInterface;
abstract class AbstractEntityService implements EntityServiceInterface
{
/**
* #var EntityManager
*/
protected $entityManager;
/**
* AbstractEntityService constructor.
* #param EntityManager $entityManager
*/
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
/**
* #return EntityManager
*/
public function getEntityManager()
{
return $this->entityManager;
}
/**
* #return \Doctrine\ORM\EntityRepository
*/
public function getEntityRepository()
{
return $this->getEntityManager()->getRepository(__CLASS__);
}
//Feel free to add more functions here that you want all of your Entities to have
}
File: repoName/src/Interfaces/EntityServiceInterface.php (note, plural "interfaces" folder, as "interface" is protected name in PHP)
<?php
namespace Company\Core\Interfaces;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Repository\RepositoryFactory;
interface EntityServiceInterface
{
/**
* #return EntityManager
*/
public function getEntityManager();
/**
* #return RepositoryFactory
*/
public function getEntityRepository();
//Feel free to add more functions here
}
File: repoName/src/Listener/ServiceLocatorAwareEntity.php
<?php
namespace Company\Core\Listener;
use Zend\Di\ServiceLocator;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class ServiceLocatorAwareEntity implements ServiceLocatorAwareInterface
{
protected $sm;
/**
* Set the service locator
*
* #param ServiceLocatorInterface $sm
*
* #return void
*/
public function setServiceLocator(ServiceLocatorInterface $sm)
{
$this->sm = $sm;
}
/**
* Get the service locator
*
* #return ServiceLocator ServiceLocator instance
*/
public function getServiceLocator()
{
return $this->sm;
}
}
In the Module.php file add following onBootstrap() function
public function onBootstrap(MvcEvent $e)
{
$sm = $e->getApplication()->getServiceManager();
/** #var EntityManager $em */
$em = $sm->get('doctrine.entitymanager.orm_default');
/** #var \Doctrine\Common\EventManager $dem */
$dem = $em->getEventManager();
/**
* Binds adding EventListener "ServiceManagerListener" to the Doctrine Event PostLoad
*/
$dem->addEventListener([Events::postLoad], new ServiceManagerListener($sm));
}
Because of this setup, make sure to had the #ORM\HasLifecycleCallbacks Doctrine Annotation to the Entity class. The above function triggers the adding of the ServiceManagerListener, which in turn triggers the remainder.
Now all that remains is using it in your own modules (repeat endlessly if needed :) ).
Here we go back to the application itself.
Make sure you do the below for each of your entities and controllers and you should be fine.
Create the following:
<?php
namespace Application\Service;
use Company\Core\Service\AbstractEntityService;
class IndexControllerService extends AbstractEntityService
{
//Yes really, this class is empty. Extends an Abstract class which may not be directly instantiated.
}
Create a factory for you IndexController
<?php
namespace Application\Factory;
use Application\Controller\IndexController;
use Application\Service\IndexControllerService;
use Doctrine\ORM\EntityManager;
use Zend\Mvc\Controller\ControllerManager;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\ServiceManager;
class IndexControllerFactory implements FactoryInterface
{
/**
* #param ServiceLocatorInterface|ControllerManager $serviceLocator
* #return IndexController
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
/** #var ServiceManager $serviceManager */
$serviceManager = $serviceLocator->getServiceLocator();
/** #var EntityManager $entityManager */
$entityManager = $serviceManager->get('doctrine.entitymanager.orm_default');
/** #var IndexControllerService $entityService */
$entityService = new IndexControllerService($entityManager);
/** #var IndexController $controller */
$controller = new IndexController($entityService);
return $controller;
}
}
Make sure that your IndexController extends the AbstractActionController
<?php
namespace Application\Controller;
use Company\Core\Controller\AbstractActionController;
class IndexController extends AbstractActionController
{
public function indexAction()
{
return [];
}
}
And make sure that you've registered this configuration in the config files using the following:
'controllers' => [
'factories' => [
IndexController::class => IndexControllerFactory::class,
],
],
'service_manager' => [
'invokables' => [
IndexControllerService::class => IndexControllerService::class,
],
],
There ya go, should be all set.
I have an abstract class for my Doctrine 2 Entity. How do I inject the Service Locator to get for example the Translator or Zend\Mvc\Controller\Plugin\Url or how do I directly inject these plugins to the abstract entity class.
The goal is to get the doctrine entity from the entity repository and manipulate the result of the entity within an abstract entity model/service.
Doctrine 2 Entity in short form:
namespace Rental\Entity;
use Doctrine\ORM\Mapping as ORM;
use Rental\Model\Rental as AbstractRental;
/**
* Rental
*
* #ORM\Entity(repositoryClass="Rental\Repository\Rental") *
* #ORM\Table(name="rental", options={"collate"="utf8_general_ci"})
*/
class Rental extends AbstractRental{
...
public function getType(){
...
}
... entity setter and getter
}
Abstract entity model:
namespace Rental\Model;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
/**
* Rental\Model\Rental
* #ORM\MappedSuperclass
* #ORM\HasLifecycleCallbacks
*/
abstract class Rental implements ServiceLocatorAwareInterface
{
protected $serviceLocator;
protected $translator;
abstract protected function getType();
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->serviceLocator = $serviceLocator;
}
public function getServiceLocator()
{
return $this->serviceLocator;
}
public function getTranslator()
{
if (!$this->translator) {
$this->translator = $this->getServiceLocator()->get('translator');
// here is the problem, because getServiceLocator is NULL
}
return $this->translator;
}
public function getTranslatedType(){
return $this->translator->translate($this->getType())
}
This is not working because the abstract class is not instantiated and so the ServiceLocatorInterface is not injected.
Here is my Controller:
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Doctrine\ORM\EntityManager;
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Doctrine\ORM\EntityManager;
class IndexController extends AbstractActionController
{
/**
* #var \Doctrine\ORM\EntityManager
*/
protected $em;
/**
* #param \Doctrine\ORM\EntityManager $em
*/
public function setEntityManager(EntityManager $em)
{
$this->em = $em;
}
/**
* #return array|\Doctrine\ORM\EntityManager|object
*/
public function getEntityManager()
{
if (NULL === $this->em) {
$this->em = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
}
return $this->em;
}
/**
* #return \Zend\View\Model\ViewModel
*/
public function indexAction()
{
...
/** #var $repository \Rental\Repository\Rental */
$repository = $this->getEntityManager()->getRepository('Rental\Entity\Rental');
/** #var $rentals array */
$rental= $repository->findBySlug($slug);
\ChromePhp::log($rental->getTranslatedType());
// is NULL
This is one way to accomplish your goal of injecting the ZF2 Service Locator into your entities that inherit from the abstract class
Remember to change/fix any namespaces
First, you'll need to register an event listener with Doctrine. You only need to listen for postLoad. To do this, instantiate the listener (which we'll define next) and pass it it's sole dependency, which is the service locator.
Module.php
// in your Module.php
use Rental\Model\Listeners\EntityInjectorListener;
public function onBootstrap(MvcEvent $e)
{
$serviceLocator = $e->getApplication()->getServiceManager();
$entityManager = $serviceLocator->get('Doctrine\ORM\EntityManager');
$entityManager->getEventManager()->addEventListener(array(\Doctrine\ORM\Events::postLoad), new EntityInjectorListener($serviceLocator));
}
Now define a listener for Doctrine to use. It should check the entity it's working on and if it inherits from your abstract class, then it should set the service locator.
// EntityInjectorListener.php
namespace \Rental\Model\Listeners;
class EntityInjectorListener
{
protected $serviceLocator;
public function __construct($serviceLocator)
{
$this->serviceLocator = $serviceLocator;
}
public function postLoad($eventArgs)
{
// check if entity is a child of abstract class
if ($eventArgs->getEntity() instanceof \Rental\Model\Rental) {
$eventArgs->getEntity()->setServiceLocator($this->serviceLocator);
}
}
}
Now within your entity you should be able to call
$this->getServiceLocator()->get('whatever-you-want-get');
To use view helpers you will to need call them like this:
$this->getServiceLocator()->get('ViewHelperManager')->get('url');