The class 'Doctrine\ORM\PersistentCollection' was not found in the chain configured namespaces - orm

I am using in project Zend Framework 2 with Doctrine 2 ORM.
I am trying to persist a many to many relation. I followed th documentation described here (manytomany).
While trying to persist data: $em->persist($form->getData()); I got the error:
"The class 'Doctrine\ORM\PersistentCollection' was not found in the chain configured namespaces".
Any suggestions ?
To be more clear I added below some code:
First I annotated entities like documentation said, for many to many relation:
/**
* #ORM\ManyToMany(targetEntity="\User\Entity\Client", mappedBy="reportSettings")
*/
private $client;
public function __construct() {
$this->client = new ArrayCollection();
}
and
/**
* #ORM\ManyToMany(targetEntity="\Statistics\Entity\ReportSettings", inversedBy="client")
* #ORM\JoinTable(name="report_client_settings",
* joinColumns={#ORM\JoinColumn(name="client_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="report_setting_id", referencedColumnName="id")})
*/
private $reportSettings;
public function __construct() {
$this->reportSettings = new ArrayCollection();
}
And in the controller
$form = new UpdateReportSettingsForm();
$form->bind($reportSettings);
$request = new Request();
if ($request->isPost()) {
$form->setData($request->getPost());
if ($form->isValid()) {
$data = $form->getData();
$em->persist($data); // here I got the error - The class 'Doctrine\ORM\PersistentCollection' was not found in the chain configured namespaces
$em->flush();
}
I also use in the form DoctrineModule\Form\Element\ObjectMultiCheckbox.
An simple var_dump($data) - return a persistent collection.

The error appear because the form was not defined correctly. The right way how to do a many to many relation i founded here - http://laundry.unixslayer.pl/2013/zf2-quest-zendform-many-to-many/

Have you added this piece of code in the beginning of your entity where you are mapping
use Doctrine\Common\Collections\ArrayCollection;

Related

Symfony3 Profiler Storage

in the Docs
http://symfony.com/doc/master/cookbook/profiler/storage.html
you still can find Information about Profiler Storage.
I just checked the code and could not find any clues how to set a custom storage.
I also find no Documentation stating this except some #legacy notes in the Original Source at 2.8.
Is there a Reason why this was removed?
I was using redis to store this data with a lifetime of eta 1hour.
Now I need to run a manual cleanup to whipe all files in that directory.
If anybody has some clues or hints on helping me with this issue are appreceated ^^
Chris
Thanks to the Tip of Matteo I was able to solve this quite flexible.
The Team of Symfony removed this, because it was hard coded into the Profiler Subsystem.
Instead of fixing this by adding a class parameter I had to solve it. :)
Ok, here is the code, If somebody needs this to.
First of all we need the Original Classes from Symfony 2.7 (at least I reused them as I only need the Redis Option ( I use it, because I can Compress the data using igbinary)
Next you need to implement a Compiler Pass.
namespace AcmeBunlde\DependencyInjection\CompilerPass;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class ProfilerCompilerPass implements CompilerPassInterface
{
/**
* You can modify the container here before it is dumped to PHP code.
*
* #param ContainerBuilder $container
*/
public function process(ContainerBuilder $container)
{
$definition = $container->getDefinition('profiler');
$definition->addArgument('%acmebundle.profiler.defaultEnabled%');
$definition->addArgument('%acmebundle.profiler.class%');
$definition->addArgument('%acmebundle.profiler.dsn%');
$definition->addArgument('%acmebundle.profiler.username%');
$definition->addArgument('%acmebundle.profiler.password%');
$definition->addArgument('%acmebundle.profiler.ttl%');
$definition->setClass('acmebundle\Profiler\Profiler');
}
}
This needs to be loaded inside the Bundle Loader:
public function build(ContainerBuilder $container)
{
...
$container->addCompilerPass(new ProfilerCompilerPass());
}
After this we need to add the Configuration for the New Profiler Storage in the DependencyInjection Folder.
namespace AcmeBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
* This is the class that validates and merges configuration from your app/config files
*
* To learn more see {#link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
* #author Chris
*/
class Configuration implements ConfigurationInterface
/**
* {#inheritdoc}
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('library');
$rootNode
->children()
->arrayNode('profiler')
->addDefaultsIfNotSet()
->children()
->booleanNode('defaultStorage')
->defaultTrue()
->end()
->scalarNode('class')
->defaultValue('')
->end()
->scalarNode('dsn')
->defaultValue('')
->end()
->scalarNode('username')
->defaultValue('')
->end()
->scalarNode('password')
->defaultValue('')
->end()
->scalarNode('ttl')
->defaultValue('3600')
->end()
->end()
->end();
return $treeBuilder();
}
}
Now set the Default Values in The Dependency Injection Bundle Loader
<?php
namespace AcmeBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
/**
* This is the class that loads and manages your bundle configuration
*
* To learn more see {#link http://symfony.com/doc/current/cookbook/bundles/extension.html}
* #author Chris
*/
class AcmeExtension extends Extension
{
/**
* {#inheritdoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
...
$container->setParameter('acmebundle.profiler.defaultEnabled',$config['profiler']['defaultStorage']);
$container->setParameter('acmebundle.profiler.class',$config['profiler']['class']);
$container->setParameter('acmebundle.profiler.dsn',$config['profiler']['dsn']);
$container->setParameter('acmebundle.profiler.username',$config['profiler']['username']);
$container->setParameter('acmebundle.profiler.password',$config['profiler']['password']);
$container->setParameter('acmebundle.profiler.ttl',$config['profiler']['ttl']);
...
}
...
}
As Last Step you need to build a basic container for adding the new Profiler Handler.
I have choosen to implement it not to complex:
<?php
namespace AcmeBundle\Profiler;
use Psr\Log\LoggerInterface;
use \Symfony\Component\HttpKernel\Profiler\Profiler as ProfilerSrc;
use Symfony\Component\HttpKernel\Profiler\ProfilerStorageInterface;
/**
* Profiler.
*/
class Profiler extends ProfilerSrc
{
public function __construct(ProfilerStorageInterface $storage, LoggerInterface $logger, $defaultEnabled=true,$class=null,$dsn=null,$username=null,$password=null,$ttl=3600)
{
if($defaultEnabled!==true)
{
$storage = new $class($dsn,$username,$password,$ttl);
}
parent::__construct($storage , $logger);
}
}
I have also added a Library to define the Constructor of the Storage Interface.
<?php
namespace AcmeBundle\Profiler;
use Symfony\Component\HttpKernel\Profiler\ProfilerStorageInterface as ProfilerStorageSource;
interface ProfilerStorageInterface extends ProfilerStorageSource
{
/**
* ProfilerStorageInterface constructor.
*
* #param $dsn
* #param $username
* #param $password
* #param $ttl
*/
public function __construct($dsn,$username,$password,$ttl);
}
All you need to do now is to define some Options in your config_dev.yml file.
acmebundle:
profiler:
defaultEnabled: false
class:CLASSNAME INCLUDING NAMESPACE
dsn: redis://localhost/1
username:
password
ttl: 3600
with defaultEnabled = true you can reenable to Original Handler.
the rest is, I believe self explaining.
username + password is from the original feature set.
(ttl == lifetime)
I hope this helps somebody else as well :)
Is marked as deprecated since 2.8 with the suppression in the 3.0. I can't find any motivation about in the PR. The doc is not yet updated as you mention.
The only suggestion is about a comment in this issue:
If you want to use your own implementation of a profiler storage,
then just override the profile.storage service.
Hope this help

Modelling aggregate root or domain service?

I want to model a wishlisting feature for my domain.
My invariants are:
You can't add product that is already in your wishlist
You can't add product that you own.
The second invariant made me wonder - should I model this feature as reconstituted Aggregate (outside of ORM because of $ownedProductIds that be fetched from UserProductRepository):
final class User extends EventSourcedAggregateRoot
{
// ...
/**
* #param UserId $userId
* #param ObjectCollection $ownedProductIds
* #param ObjectCollection $wishlistedProductIds
* #return $this
*/
public static function reconstituteFrom(
UserId $userId,
ObjectCollection $ownedProductIds,
ObjectCollection $wishlistedProductIds
)
{
$user = new User();
$user->userId = $userId;
$user->ownedProductIds = $ownedProductIds;
$user->wishlistedProductIds = $wishlistedProductIds;
return $user;
}
/**
* #param Product $product
* #throws ProductAlreadyPurchased Thrown when trying to add already bought product
* #throws ProductAlreadyWishlisted Thrown when trying to add already wishlisted product
*/
public function addProductToWishlist(Product $product)
{
$productId = $product->getId();
if ($this->ownedProductIds->contains($productId)) {
throw new ProductAlreadyPurchased($this->userId, $productId);
}
if ($this->wishlistedProductIds->contains($productId)) {
throw new ProductAlreadyWishlisted($this->userId, $productId);
}
$this->apply(new ProductWishlisted($this->userId, $product));
}
// ...
}
or rather create a stateless domain service:
final class Wishlist
{
public function addProductToWishlist(Product $product, UserId $userId)
{
$ownedProductids = $this->userProductRepository->findProductsOfUser($userId);
$wishlistedProductsIds = $this->userWishlistProductIdRepository->findProductsOfUser($userId);
// business rules as in User class
}
}
As User has all the information needed to enforce the invariants, I would leave it there. I typically only create Domain Services when some operation doesn't seem to belong in one entity (TransferMoney() method not fitting in Account class is the poster child for this).
Note though that your model is currently really simplistic. As it is, it may make sense to name the aggregate User, but in a real situation chances are you will make breakthroughs that completely change it.

Kohana ORM module not working

I'm learning Kohana at the mo and encountering the following error when trying to extend a model to use ORM.
Declaration of Model_Message::create() should be compatible with that of Kohana_ORM::create()
I've enabled the orm in my bootstrap along with the database. The error highlights the following line on the error dump.
class Model_Message extends ORM {
And here is the model code I'm using and failing with...
<?php defined('SYSPATH') or die('No direct script access');
/**
* Message modal
* Handles CRUD for user messages
*/
class Model_Message extends ORM {
/**
* Adds a new message for a user
*
* #param int user_id
* #param string user's message
* #return self
*/
public function create($user_id, $content)
{
$this->clear();
$this->user_id = $user_id;
$this->content = $content;
$this->date_published = time();
return $this->save();
}
}
I've been going through the api documentation and everything is saying that this way of implementing the orm from the model is the correct way to do so. Any pointers would be great.
You need to rename your method (create_message for example) or make it compatible with ORM (because it has the it's own create method which you try to override).

Doctrine 2.1 Persist entity in preUpdate lifeCycleCallback

I'm struggling with the following, in a entity class I have a preUpdate lifeCycleCallback which has to persist a new entity before it flushes the changes for a auditTrail.
In preRemove and prePersist this works perfectly but in preUpdate nothing happends. If I call flush myself it goes in a recursive loop.
According to the Google groups for doctrine-user putting it in onFlush should be a option but in that event I can't access the old values of the entity to save this old values in a new other entity for the audittrail.
Some small example what i'm trying to archive:
<?php
/**
* #Entity
* #HasLifeCycleCallbacks
*/
class someEntity {
... annotations ...
/**
* #PreUpdate
*/
public function addAuditTrail() {
$em = \Zend_Registry::get('doctrine')->getEntityManager();
$entity = new AuditTrail();
$entity->action = 'update';
$entity->someField = $this->someField;
$em->persist($entity); //this just doesn't do anything :-(
}
}
?>
It's not real code, just something to illustrate you what I want. I also tried something like this:
$em->getUnitOfWork()->computeChangeSet($em->getClassMetaData(get_class($entity)), $entity);
Which should work according to this topic: http://groups.google.com/group/doctrine-user/browse_thread/thread/bd9195f04857dcd4
If I call the flush again but that causes Apache to crash because of some infinite loop.
Anyone who got ideas for me? Thanks!
You should never use the entitymanager inside your entities. If you would like to add audit trails, you should map the "SomeEntity" entity to the "AuditTrail" entity and do something like
/**
* #PreUpdate
*/
public function addAuditTrail() {
$entity = new AuditTrail();
$entity->action = 'update';
$entity->someField = $this->someField;
$this->autitTrail->add($entity);
}
If you set the cascade option on the mapping, it will get persisted when you persist "SomeEntity".
I had the same problem in the preUpdate method of an EventListener. I solved this by storing the new entity in a property and moving the new persist() and flush() calls to the postUpdate method.
class someEntity {
... annotations ...
protected $store;
/**
* #PreUpdate
*/
public function addAuditTrail() {
//$em = \Zend_Registry::get('doctrine')->getEntityManager();
$entity = new AuditTrail();
$entity->action = 'update';
$entity->someField = $this->someField;
// replaces $em->persist($entity);
$this->store = $entity;
}
/**
* #PostUpdate
*/
public function saveAuditTrail() {
$em = \Zend_Registry::get('doctrine')->getEntityManager();
$em->persist($this->store);
$em->flush();
}
}
entitymanager->persist() will not work inside the preUpdate method.
Instead of that, you can save the AuditTrail data to session and after the flush of the 'SomeEntity', take the data from session and perform entitymanager->persist(...) and entitymanager->flush()

Doctrine2 ArrayCollection

Ok, I have a User entity as follows
<?php
class User
{
/**
* #var integer
* #Id
* #Column(type="integer")
* #GeneratedValue
*/
protected $id;
/**
* #var \Application\Entity\Url[]
* #OneToMany(targetEntity="Url", mappedBy="user", cascade={"persist", "remove"})
*/
protected $urls;
public function __construct()
{
$this->urls = new \Doctrine\Common\Collections\ArrayCollection();
}
public function addUrl($url)
{
// This is where I have a problem
}
}
Now, what I want to do is check if the User has already the $url in the $urls ArrayCollection before persisting the $url.
Now some of the examples I found says we should do something like
if (!$this->getUrls()->contains($url)) {
// add url
}
but this doesn't work as this compares the element values. As the $url doesn't have id value yet, this will always fail and $url will be dublicated.
So I'd really appreciate if someone could explain how I can add an element to the ArrayCollection without persisting it and avoiding the duplication?
Edit
I have managed to achive this via
$p = function ($key, $element) use ($url)
{
if ($element->getUrlHash() == $url->getUrlHash()) {
return true;
} else {
return false;
}
};
But doesn't this still load all urls and then performs the check? I don't think this is efficient as there might be thousands of urls per user.
This is not yet possible in a "domain driven" way, ie. just using objects. You should execute a query to check for the existance:
SELECT count(u.id) FROM User u WHERE ?1 IN u.urls AND u.id = ?2
With Doctrine 2.1 this will be possible using a combination of two new features:
Extra Lazy Collections
#IndexBy for collections, so you would define #OneToMany(targetEntity="Url", indexBy="location")
ExtraLazy Collection Support for index by using ->contains().
Points 1 and 2 are already implemented in Doctrine 2 master, but 3 is still missing.
You should try using the exists method on the collection and manually compare values.