Symfony + Gedmo Doctrine with translatable slug - php

I have read through Gedmo Doctrine Extensions - Sluggable + Translatable Yaml Configuration and https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/sluggable.md#using-translationlistener-to-translate-our-slug
I am still not getting it to work. Translation works for all the other fields I have set but it doesn't work for the slug.
Have I missed something?
I have set the priority of sluggable listener to 1
/MyBundle/config/services.yml
gedmo.listener.sluggable:
class: Gedmo\Sluggable\SluggableListener
tags:
- { name: doctrine.event_subscriber, connection: default, priority: 1 }
calls:
- [ setAnnotationReader, [ #annotations.cached_reader] ]
/MyBundle/Entity/MyEntity.php
/**
* #Gedmo\Translatable
* #Gedmo\Slug(fields={"title"})
* #ORM\Column(length=64, unique=true, nullable=false)
*/
private $slug;
/**
* #Gedmo\Locale
*
*/
protected $locale;
public function setTranslatableLocale($locale){
$this->locale = $locale;
}
The default locale is 'de'.
/MyBundle/Controller/MyController.php
/** #var EntityManager $em */
$em = $this->getDoctrine()->getManager();
$object = $em->getRepository('MyEntity')->find($id)
$evm = new EventManager();
$sluggableListener = new SluggableListener();
$evm->addEventSubscriber($sluggableListener);
$translatableListener = new TranslatableListener();
$translatableListener->setTranslatableLocale('en');
$evm->addEventSubscriber($translatableListener);
$object->setTranslatableLocale('en);
$em->refresh($object);
$em->persist($object);
$em->flush;
.....

Related

JMS serializer ignores doctrine entity id

I tried map data from a given array to an object with the jms serializer (in a unit test) to test a doctrine entity:
Given is a simple entity class:
/**
* CashPosition
*/
class CashPosition
{
/**
* #var integer
*/
protected $cashPositionId;
/**
* #var \DateTime
*/
protected $date;
/**
* #var float
*/
protected $value;
/**
* Get cashPositionId
*
* #return integer
*/
public function getCashPositionId()
{
return $this->cashPositionId;
}
/**
* Set date
*
* #param \DateTime $date
*
* #return $this
*/
public function setDate($date)
{
$this->date = $date;
return $this;
}
/**
* Get date
*
* #return \DateTime
*/
public function getDate()
{
return $this->date;
}
/**
* Set value
*
* #param string $value
*
* #return $this
*/
public function setValue($value)
{
$this->value = $value;
return $this;
}
/**
* Get value
*
* #return float
*/
public function getValue()
{
return $this->value;
}
}
I defined the serialization under Resources\config\serializer\Entity.CashPosition.yml
MyBundle\Entity\CashPosition:
exclusion_policy: ALL
access_type: public_method
properties:
cashPositionId:
exclude: false
expose: true
type: integer
access_type: property
date:
exclude: false
expose: true
type: DateTime<'Y-m-d'>
value:
exclude: false
expose: true
type: float
And tried to cover this with a serialization test:
public function testSerialization()
{
$data = [
'cashPositionId' => 1,
'date' => date('Y-m-d'),
'value' => 1.0,
];
/* #var $serializer Serializer */
$serializer = $this->container->get('serializer');
$cashPosition = $serializer->fromArray($data, CashPosition::class);
$this->assertInstanceOf(CashPosition::class, $cashPosition);
$this->assertEquals($data, $serializer->toArray($cashPosition));
}
But the test fails since the fromArray method does not set the cashPositionId. I tried some different configurations with the access type but had no luck. I'm not sure what's the problem here.
I'm using the following version of jms serializer:
jms/metadata 1.6.0 Class/method/property metadata management in PHP
jms/parser-lib 1.0.0 A library for easily creating recursive-descent parsers.
jms/serializer 1.6.2 Library for (de-)serializing data of any complexity; supports XML, JSON, and YAML.
jms/serializer-bundle 1.4.0 Allows you to easily serialize, and deserialize data of any complexity
Hello i think you miss the serialized_name property for cashPositionId, by default jms will translate properties from camel case to snake case.
JMS Doc

Doctrine: update column on insert in other table

I have 2 entities: Service and Session with one-to-many relationship
class Service{
/**
* #var int
*
* #ORM\Column(name="avg_score", type="integer")
*/
private $avgScore;
/**
* #ORM\OneToMany(targetEntity="Session", mappedBy="service")
*/
private $sessionList;
}
class Session{
/**
* #ORM\ManyToOne(targetEntity="Service", inversedBy="sessionList")
* #ORM\JoinColumn(name="service_id", referencedColumnName="id")
*/
private $service;
/**
* #var int
*
* #ORM\Column(name="score", type="integer", nullable=true)
*/
private $score;
}
With Doctrine QueryBuilder how can I update $avgScore of Service entity everytime new Session with $score is created?
This is what I tried to do:
$qb = $this->getDoctrine()->getEntityManager()->createQueryBuilder();
$q = $qb->update('AppBundle:Service', 's')
->join('AppBundle:Session', 'ss')
->addSelect('avg(ss.score) as score_avg')
->groupBy('ss.service')
->set('s.avgScore', 'score_avg')
->where('s.id = ?1')
->setParameter(1, $service->getId())
->getQuery();
$q->execute();
You need to create a Doctrine Events Listener
services:
my.listener:
class: AppBundle\EventListener\AvgScoreUpdater
tags:
- { name: doctrine.event_listener, event: prePersist }
And then in AvgScoreUpdater implement logic:
class AvgScoreUpdater
{
public function prePersist(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
if (!($entity instanceof Session) || !$entity->getScore()) {
return;
}
$entityManager = $args->getEntityManager();
$service = $entity->getService();
// Then realize logic to update avg_score on a service
}
}

How to override bundled Doctrine repository in Symfony

I have an independent Symfony bundle (installed with Composer) with entities and repositories to share between my applications that connect same database.
Entities are attached to every applications using configuration (yml shown):
doctrine:
orm:
mappings:
acme:
type: annotation
dir: %kernel.root_dir%/../vendor/acme/entities/src/Entities
prefix: Acme\Entities
alias: Acme
Well, it was the easiest way to include external entities in application, but looks a bit ugly.
Whenever I get repository from entity manager:
$entityManager->getRepository('Acme:User');
I get either preconfigured repository (in entity configuration) or default Doctrine\ORM\EntityRepository.
Now I want to override bundled (or default) repository class for a single entity. Is there any chance to do it with some configuration/extension/etc?
I think, the best looking way is something like:
doctrine:
orm:
....:
Acme\Entities\User:
repositoryClass: My\Super\Repository
Or with tags:
my.super.repository:
class: My\Super\Repository
tags:
- { name: doctrine.custom.repository, entity: Acme\Entities\User }
You can use LoadClassMetadata event:
class LoadClassMetadataSubscriber implements EventSubscriber
{
/**
* #inheritdoc
*/
public function getSubscribedEvents()
{
return [
Events::loadClassMetadata
];
}
/**
* #param LoadClassMetadataEventArgs $eventArgs
*/
public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
{
/**
* #var \Doctrine\ORM\Mapping\ClassMetadata $classMetadata
*/
$classMetadata = $eventArgs->getClassMetadata();
if ($classMetadata->getName() !== 'Acme\Entities\User') {
return;
}
$classMetadata->customRepositoryClassName = 'My\Super\Repository';
}
}
Doctrine Events
Entities are attached to every applications using configuration (yml shown):
Well, it was the easiest way to include external entities in application, but looks a bit ugly.
You can enable auto_mapping
Works for Doctrine versions <2.5
In addition to Artur Vesker answer I've found another way: override global repository_factory.
config.yml:
doctrine:
orm:
repository_factory: new.doctrine.repository_factory
services.yml:
new.doctrine.repository_factory:
class: My\Super\RepositoryFactory
Repository Factory:
namespace My\Super;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Repository\DefaultRepositoryFactory;
class RepositoryFactory extends DefaultRepositoryFactory
{
/**
* #inheritdoc
*/
protected function createRepository(EntityManagerInterface $entityManager, $entityName)
{
if ($entityName === Acme\Entities\User::class) {
$metadata = $entityManager->getClassMetadata($entityName);
return new ApplicationRepository($entityManager, $metadata);
}
return parent::createRepository($entityManager, $entityName);
}
}
No doubt implementing LoadClassMetadataSubscriber is a better way.
With current symfony 5.3 and doctrine 2.9.5
In your configuration define the service and doctrine.orm.repository_factory:
doctrine:
orm:
#Replace repository factory
repository_factory: 'MyBundle\Factory\RepositoryFactory'
services:
MyBundle\Factory\RepositoryFactory:
arguments: [ '#router', '#translator', '%kernel.secret%' ]
Add you MyBundle/Factory/RepositoryFactory.php file:
<?php declare(strict_types=1);
namespace MyBundle\Factory;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Repository\RepositoryFactory as RepositoryFactoryInterface;
use Doctrine\Persistence\ObjectRepository;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* This factory is used to create default repository objects for entities at runtime.
*/
final class RepositoryFactory implements RepositoryFactoryInterface {
/**
* The list of EntityRepository instances
*
* #var ObjectRepository[]
*/
private $repositoryList = [];
/**
* The kernel secret
*
* #var string
*/
private $secret;
/**
* The RouterInterface instance
*
* #var RouterInterface
*/
private $router;
/**
* The TranslatorInterface instance
*
* #var TranslatorInterface
*/
private $translator;
/**
* Initializes a new RepositoryFactory instance
*
* #param RouterInterface $router The router instance
* #param TranslatorInterface $translator The TranslatorInterface instance
* #param string $secret The kernel secret
*/
public function __construct(RouterInterface $router, TranslatorInterface $translator, string $secret) {
//Set router
$this->router = $router;
//Set secret
$this->secret = $secret;
//Set translator
$this->translator = $translator;
}
/**
* {#inheritdoc}
*/
public function getRepository(EntityManagerInterface $entityManager, $entityName): ObjectRepository {
//Set repository hash
$repositoryHash = $entityManager->getClassMetadata($entityName)->getName() . spl_object_hash($entityManager);
//With entity repository instance
if (isset($this->repositoryList[$repositoryHash])) {
//Return existing entity repository instance
return $this->repositoryList[$repositoryHash];
}
//Store and return created entity repository instance
return $this->repositoryList[$repositoryHash] = $this->createRepository($entityManager, $entityName);
}
/**
* Create a new repository instance for an entity class
*
* #param EntityManagerInterface $entityManager The EntityManager instance.
* #param string $entityName The name of the entity.
*/
private function createRepository(EntityManagerInterface $entityManager, string $entityName): ObjectRepository {
//Get class metadata
$metadata = $entityManager->getClassMetadata($entityName);
//Get repository class
$repositoryClass = $metadata->customRepositoryClassName ?: $entityManager->getConfiguration()->getDefaultRepositoryClassName();
//Return repository class instance
//XXX: router, translator and secret arguments will be ignored by default
return new $repositoryClass($entityManager, $metadata, $this->router, $this->translator, $this->secret);
}
}
Then define your MyBundle/Repository/EntityRepository.php:
<?php declare(strict_types=1);
namespace MyBundle\Repository;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository as BaseEntityRepository;
use Doctrine\ORM\Mapping\ClassMetadata;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* EntityRepository
*
* {#inheritdoc}
*/
class EntityRepository extends BaseEntityRepository {
/**
* The RouterInterface instance
*
* #var RouterInterface
*/
protected RouterInterface $router;
/**
* The table keys array
*
* #var array
*/
protected array $tableKeys;
/**
* The table values array
*
* #var array
*/
protected array $tableValues;
/**
* The TranslatorInterface instance
*
* #var TranslatorInterface
*/
protected TranslatorInterface $translator;
/**
* The kernel secret
*
* #var string
*/
protected string $secret;
/**
* Initializes a new LocationRepository instance
*
* #param EntityManagerInterface $manager The EntityManagerInterface instance
* #param ClassMetadata $class The ClassMetadata instance
* #param RouterInterface $router The router instance
* #param TranslatorInterface $translator The TranslatorInterface instance
* #param string $secret The kernel secret
*/
public function __construct(EntityManagerInterface $manager, ClassMetadata $class, RouterInterface $router, TranslatorInterface $translator, string $secret) {
//Call parent constructor
parent::__construct($manager, $class);
//Set secret
$this->secret = $secret;
//Set router
$this->router = $router;
//Set slugger
$this->slugger = $slugger;
//Set translator
$this->translator = $translator;
//Get quote strategy
$qs = $manager->getConfiguration()->getQuoteStrategy();
$dp = $manager->getConnection()->getDatabasePlatform();
//Set quoted table names
//XXX: remember to place longer prefix before shorter to avoid strange replacings
$tables = [
'MyBundle:UserGroup' => $qs->getJoinTableName($manager->getClassMetadata('MyBundle:User')->getAssociationMapping('groups'), $manager->getClassMetadata('MyBundle:User'), $dp),
'MyBundle:Group' => $qs->getTableName($manager->getClassMetadata('MyBundle:Group'), $dp),
'MyBundle:User' => $qs->getTableName($manager->getClassMetadata('MyBundle:User'), $dp),
//XXX: Set limit used to workaround mariadb subselect optimization
':limit' => PHP_INT_MAX,
"\t" => '',
"\n" => ' '
];
//Set quoted table name keys
$this->tableKeys = array_keys($tables);
//Set quoted table name values
$this->tableValues = array_values($tables);
}
}
Then simply extend it in MyBundle/Repository/UserRepository.php:
<?php declare(strict_types=1);
namespace MyBundle\Repository;
/**
* UserRepository
*/
class UserRepository extends EntityRepository {
}

Symfony Gedmo Blameable not working

Did any one ever find an solution for this issue?
I'm having the same issue.
My config.yml:
# Doctrine Configuration
doctrine:
dbal:
driver: "%database_server%"
host: "%database_host%"
port: "%database_port%"
dbname: "%database_name%"
user: "%database_user%"
password: "%database_password%"
charset: UTF8
# if using pdo_sqlite as your database driver:
# 1. add the path in parameters.yml
# e.g. database_path: "%kernel.root_dir%/data/data.db3"
# 2. Uncomment database_path in parameters.yml.dist
# 3. Uncomment next line:
# path: "%database_path%"
orm:
auto_generate_proxy_classes: "%kernel.debug%"
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: true
#Gedmo Package extension for Symfony and Doctrine
mappings:
gedmo_tree:
type: annotation
prefix: Gedmo\Tree\Entity
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity"
alias: GedmoTree
is_bundle: false
gedmo_sortable:
type: annotation
prefix: Gedmo\Sortable\Entity
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Entity"
alias: GedmoTree
is_bundle: false
[...]
stof_doctrine_extensions:
default_locale: "%locale%"
translation_fallback: true
orm:
default:
timestampable: true
blameable: true
My doctrine_extension.yml is included in the config file:
services:
extension.listener:
class: Omega\HomeBundle\Library\Listener\DoctrineExtensionListener
calls:
- [ setContainer, [#service_container]]
tags:
# loggable hooks user username if one is in security context
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
# Doctrine Extension listeners to handle behaviors
gedmo.listener.tree:
class: Gedmo\Tree\TreeListener
tags:
- { name: doctrine.event_subscriber, connection: default }
calls:
- [ setAnnotationReader, [ #annotation_reader ] ]
gedmo.listener.sortable:
class: Gedmo\Sortable\SortableListener
tags:
- { name: doctrine.event_subscriber, connection: default }
calls:
- [ setAnnotationReader, [ #annotation_reader ] ]
gedmo.listener.timestampable:
class: Gedmo\Timestampable\TimestampableListener
tags:
- { name: doctrine.event_subscriber, connection: default }
calls:
- [ setAnnotationReader, [ #annotation_reader ] ]
gedmo.listener.loggable:
class: Gedmo\Loggable\LoggableListener
tags:
- { name: doctrine.event_subscriber, connection: default }
calls:
- [ setAnnotationReader, [ #annotation_reader ] ]
gedmo.listener.blameable:
class: Gedmo\Blameable\BlameableListener
tags:
- { name: doctrine.event_subscriber, connection: default }
calls:
- [ setAnnotationReader, [ #annotation_reader ] ]
- [ setUserValue, [ #security.token_storage ] ]
I created myself an trait to handle the created, updated, updated_by and createdby fields:
namespace HomeBundle\Traits;
use Doctrine\ORM\Mapping as ORM;
use Omega\UserBundle\Entity\Users;
use Gedmo\Mapping\Annotation as Gedmo;
trait LogableTrait
{
/**
* #var Users
* #Gedmo\Blameable(on="create")
* #ORM\ManyToOne(targetEntity="UserBundle\Entity\Users")
* #ORM\JoinColumn(name="log_created_by", referencedColumnName="id")
*/
protected $CreatedBy;
/**
* #var Users
* #Gedmo\Blameable(on="update")
* #ORM\ManyToOne(targetEntity="UserBundle\Entity\Users")
* #ORM\JoinColumn(name="log_updated_by", referencedColumnName="id")
*/
protected $UpdatedBy;
/**
* #Gedmo\Timestampable(on="create")
* #ORM\Column(name="created", type="datetime")
* #var \DateTime
*/
protected $created;
/**
* #Gedmo\Timestampable(on="create")
* #ORM\Column(name="updated", type="datetime")
* #var \DateTime
*/
protected $updated;
/**
* #return Users
*/
public function getCreatedBy ()
{
return $this->CreatedBy;
}
/**
* #param Users $CreatedBy
*
* #return $this
*/
public function setCreatedBy (Users $CreatedBy )
{
$this->CreatedBy = $CreatedBy;
return $this;
}
/**
* #return Users
*/
public function getUpdatedBy ()
{
return $this->UpdatedBy;
}
/**
* #param Users $UpdatedBy
*
* #return $this
*/
public function setUpdatedBy (Users $UpdatedBy )
{
$this->UpdatedBy = $UpdatedBy;
return $this;
}
}
But everytime that I use this Bundle I get:
The class 'Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage' was not found in the chain configured namespaces Gedmo\Tree\Entity, Gedmo\Sortable\Entity, JMS\JobQueueBundle\Entity, AccountingBundle\Entity, DocumentsBundle\Entity, EavBundle\Entity, HomeBundle\Entity, UserBundle\Entity, CustomerBundle\Entity, Jns\Bundle\XhprofBundle\Entity
I hope some body can help me.
for any one that Is having th same issue like me that the blamable feature is not working:
My solution was to implement the BlamableListener with an different approach:
namespace HomeBundle\Library;
use Doctrine\Common\NotifyPropertyChanged;
use Gedmo\Exception\InvalidArgumentException;
use Gedmo\Timestampable\TimestampableListener;
use Gedmo\Blameable\Mapping\Event\BlameableAdapter;
use Gedmo\Blameable\Mapping\Driver\Annotation;
/**
* The Blameable listener handles the update of
* dates on creation and update.
*
* #author Gediminas Morkevicius <gediminas.morkevicius#gmail.com>
* #license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
class BlameableListener extends TimestampableListener
{
protected $user;
/**
* Get the user value to set on a blameable field
*
* #param object $meta
* #param string $field
*
* #return mixed
*/
public function getUserValue($meta, $field)
{
if ($meta->hasAssociation($field)) {
if (null !== $this->user && ! is_object($this->user)) {
throw new InvalidArgumentException("Blame is reference, user must be an object");
}
$user = $this->user->getToken()->getUser();
if(!is_object($user))
{
return null;
}
return $user;
}
// ok so its not an association, then it is a string
if (is_object($this->user)) {
if (method_exists($this->user, 'getUsername')) {
return (string) $this->user->getUsername();
}
if (method_exists($this->user, '__toString')) {
return $this->user->__toString();
}
throw new InvalidArgumentException("Field expects string, user must be a string, or object should have method getUsername or __toString");
}
return $this->user;
}
/**
* Set a user value to return
*
* #param mixed $user
*/
public function setUserValue($user)
{
$this->user = $user;
}
/**
* {#inheritDoc}
*/
protected function getNamespace()
{
return __NAMESPACE__;
}
/**
* Updates a field
*
* #param object $object
* #param BlameableAdapter $ea
* #param $meta
* #param $field
*/
protected function updateField($object, $ea, $meta, $field)
{
$property = $meta->getReflectionProperty($field);
$oldValue = $property->getValue($object);
$newValue = $this->getUserValue($meta, $field);
//if blame is reference, persist object
if ($meta->hasAssociation($field) && $newValue) {
$ea->getObjectManager()->persist($newValue);
}
$property->setValue($object, $newValue);
if ($object instanceof NotifyPropertyChanged) {
$uow = $ea->getObjectManager()->getUnitOfWork();
$uow->propertyChanged($object, $field, $oldValue, $newValue);
}
}
}
adjust the service for the blamable:
gedmo.listener.blameable:
class: HomeBundle\Library\BlameableListener
tags:
- { name: doctrine.event_subscriber, connection: default }
calls:
- [ setAnnotationReader, [ #annotation_reader ] ]
- [ setUserValue, [ #security.token_storage ] ]
you need to copy the mapping library to the same location as the listener itself. Adjust the namespaces and it works. It seems that some structures changed in symfony 2.7 so the plugin no longer works out of the box.
If you want to update the updated_by field, you must specify the field so that when you update it, do so in updated_by. For example:
/**
* #var \DateTime $updated
*
* #Gedmo\Timestampable(on="update")
* #ORM\Column(type="datetime", nullable=true)
*/
protected $updated;
/**
* #var string $updatedBy
*
* #Gedmo\Blameable(on="update", field="updated")
* #ORM\Column(type="string", nullable=true)
*/
protected $updatedBy;
Pay attention to field="updated"
Just create DoctrineExtensionSubscriber
Manually tag the listeners
Gedmo\Loggable\LoggableListener:
tags:
- { name: doctrine_mongodb.odm.event_subscriber }
Create DoctrineExtensionSubscriber
<?php
namespace App\EventSubscriber;
use Gedmo\Blameable\BlameableListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class DoctrineExtensionSubscriber implements EventSubscriberInterface
{
/**
* #var BlameableListener
*/
private $blameableListener;
/**
* #var TokenStorageInterface
*/
private $tokenStorage;
/**
* #var TranslatableListener
*/
private $translatableListener;
/**
* #var LoggableListener
*/
private $loggableListener;
public function __construct(
BlameableListener $blameableListener,
TokenStorageInterface $tokenStorage,
TranslatableListener $translatableListener,
LoggableListener $loggableListener
) {
$this->blameableListener = $blameableListener;
$this->tokenStorage = $tokenStorage;
$this->translatableListener = $translatableListener;
$this->loggableListener = $loggableListener;
}
public static function getSubscribedEvents()
{
return [
KernelEvents::REQUEST => 'onKernelRequest',
KernelEvents::FINISH_REQUEST => 'onLateKernelRequest'
];
}
public function onKernelRequest(): void
{
if ($this->tokenStorage !== null &&
$this->tokenStorage->getToken() !== null &&
$this->tokenStorage->getToken()->isAuthenticated() === true
) {
$this->blameableListener->setUserValue($this->tokenStorage->getToken()->getUser());
}
}
public function onLateKernelRequest(FinishRequestEvent $event): void
{
$this->translatableListener->setTranslatableLocale($event->getRequest()->getLocale());
}
}

Doctrine 2 Entity Error

I would like to ask, if anybody obtained such error before. Since I am stuck for 2 hours fixing bugs with doctrine already. So any kind of of help would be appreciate.
To the point. I am fighting with doctrine Entity Manager I can't make it work.
I created entity and doctrine class to work with, but I am getting an error all the time:
Fatal error: Uncaught exception 'Doctrine\ORM\Mapping\MappingException' with message 'Class "MSP\Model\Entity\Category" is not a valid entity or mapped super class.' in /home/dariss/www/dom/php/MenuSiteProject/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappingException.php:216 Stack trace: #0 /home/dariss/www/dom/php/MenuSiteProject/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php(87): Doctrine\ORM\Mapping\MappingException::classIsNotAValidEntityOrMappedSuperClass('MSP\Model\Entit...') #1 /home/dariss/www/dom/php/MenuSiteProject/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php(113): Doctrine\ORM\Mapping\Driver\AnnotationDriver->loadMetadataForClass('MSP\Model\Entit...', Object(Doctrine\ORM\Mapping\ClassMetadata)) #2 /home/dariss/www/dom/php/MenuSiteProject/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php(318): Doctrine\ORM\Mapping\ClassMetadataFactory->doLoadMetadata(Object(Doctrine\ORM\Mapping\ClassMetadata), NULL, false, Array) in /home/dariss/www/dom/php/MenuSiteProject/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappingException.php on line 216
My entity class.
namespace MSP\Model\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Category
*
* #ORM\Entity
* #ORM\Table(name="category")
*/
class Category {
/**
* #var integer $id
*
* #ORM\Column(name="id", type="int", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="SEQUENCE")
* #ORM\SequenceGenerator(sequenceName="category_id_seq", allocationSize=1, initialValue=1)
*/
private $id;
/**
* #var String $name
*
* #ORM\Column(name="name", type"string", length=50, nullable=true)
*/
private $name;
/**
* #var integer $parent
*
* #ORM\Column(name="parent", type="int", nullable=true)
*/
private $parent;
/**
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* #param $name
* #return Category
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* #return String
*/
public function getName()
{
return $this->name;
}
/**
* #param int $parent
* #return Category
*/
public function setParent($parent)
{
$this->parent = $parent;
return $this;
}
/**
* #return int
*/
public function getParent()
{
return $this->parent;
}
Doctrine class:
namespace MSP\Helper;
use Doctrine\Common\ClassLoader,
Doctrine\ORM\Configuration,
Doctrine\ORM\EntityManager,
Doctrine\Common\Cache\ArrayCache,
Doctrine\DBAL\Logging\EchoSQLLogger;
class Doctrine{
public $em = null;
public function __construct()
{
require_once __DIR__.'/../../../vendor/doctrine/common/lib/Doctrine/Common/ClassLoader.php';
$doctrineClassLoader = new ClassLoader('Doctrine', '/');
$doctrineClassLoader->register();
$entitiesClassLoader = new ClassLoader('MSP\Model\Entity', '/../Model/Entity');
$entitiesClassLoader->register();
$proxiesClassLoader = new ClassLoader('Proxies', '/../Proxies/');
$proxiesClassLoader->register();
// Set up caches
$config = new Configuration;
$cache = new ArrayCache;
$config->setMetadataCacheImpl($cache);
$driverImpl = $config->newDefaultAnnotationDriver(array('/../Model/Entity'), true);
$config->setMetadataDriverImpl($driverImpl);
$config->setQueryCacheImpl($cache);
$config->setQueryCacheImpl($cache);
// Proxy configuration
$config->setProxyDir('/proxies');
$config->setProxyNamespace('Proxies');
// Set up logger
$logger = new EchoSQLLogger;
//$config->setSQLLogger($logger);
$config->setAutoGenerateProxyClasses( TRUE );
$iniParser = new \MSP\Helper\IniParser();
$configuration = $iniParser->getConfig();
// Database connection information
$connectionOptions = array(
'driver' => $configuration['driver'],
'user' => $configuration['username'],
'password' => $configuration['password'],
'dbname' => $configuration['dbname'],
'host' => $configuration['host']
);
// Create EntityManager
$this->em = EntityManager::create($connectionOptions, $config);
}
}
Test file:
$doctrine = new MSP\Helper\Doctrine();
$doctrine->em->find('MSP\Model\Entity\Category', 1);
You need to drop the #ORM prefixes when you annotate the entities.
But I followed the example in the Symfony 2 documentation? Yep. For Symfony 2 you need to use #ORM. But your test cases uses "pure" doctrine which means no #ORM.
If you really need to run your stuff using pure doctrine then consider using yaml notation.
Why does S2 use #ORM? It's a long sad story but basically it introduced the #ORM prefix so other annotations would not clash with Doctrine.
Can you tweak the Doctrine configuration to allow the user of #ORM? Yep. But I forget how. You can search for it.
Bottom line: Consider just using the Symfony 2 Doctrine service. It's easier.
You set an absolute path /../Model/Entity.
You need to set ./../Model/Entity

Categories