Symfony2 Doctrine2 exceeding memory limit, Allowed memory size of 1073741824 bytes exhausted - symfony

I have a relational database for posts.
Post Table <-- OneToMany
Post cat Table <-- ManyToOne
Category Table <-- OneToMany
If I use the Doctrine #ORM to join the tables, in Entities using annotation. I get a white screen and in the error logs show the error:
emergency.EMERGENCY: Allowed memory size of 1073741824 bytes exhausted (tried to allocate 1052508160 bytes) {"type":1,"file":"/[PATH TO SYMFONY]/vendor/twig/twig/lib/Twig/Extension/Debug.php","line":66}
I have upped the memory limit several times, from 64M to 1024M.
Has anyone else found this problem? I think that a file executing > a Gig of memory is no good.
If I write the query using the query builder I get the results I expect. I think if I could get the Doctrine relational mapping to work this would be better. does any one have an opinion on this?
I would love a bit of advice on this matter.
Thanks In advance.
Joe
------------ Edit in response to comment ---------------------------------------
Thanks for your comment #Cerad. There are only about 10 rows in the database. Also i'm in app_dev.php.
Here are some excerpts form my files.
post table
class Post
{
//... ^ table collumns
/**
* #ORM\OneToMany(targetEntity="PostCats", mappedBy="Post")
*/
protected $PostCats;
public function __construct()
{
$this->PostCats = new ArrayCollection();
}
}
post cat joining table.
class PostCats
{
//... ^ table collumns
/**
* #ORM\ManyToOne(targetEntity="Post", inversedBy="PostCats")
* #ORM\JoinColumn(name="postid", referencedColumnName="id")
*/
protected $Post;
}
the controller
$posts = $this->getDoctrine()
->getRepository('comPostBundle:Post')
->find(7);
if (!$posts) {
throw $this->createNotFoundException(
'No product found for id '.$posts
);
}
return new Response(print_r($posts))
Result.... White screen...
I have also tried returning the results dumped into a twig template.
Do you think its ok to skip the Doctrine relational mapping and just write joins in the entity repositories?

So The problem has been solved thanks to #Cerad.
The issue was that I was ether doing a print_r() in the PHP, or a {{ dump() }} in the twig template. these functions do not like entities or displaying large arrays/objects.
now I am just calling what parts of the returned values I want and not dumping the whole data. and it works fine!.
EDIT:
This works for dumping data
\Doctrine\Common\Util\Debug::dump($object);

Related

Akeneo 2.2.8 : How can I get the original attribute data in the akeneo.storage.pre_save event?

I'm using Akeneo 2.2.8 and I'm trying to use the akeneo.storage.pre_save-event to compare the original product data with the new data provided. I do this by subscribing to the akeneo.storage.pre_save-event:
In event_subscribers.yml:
parameters:
vendor.bundle.event_subscriber.product_save.class: Vendor\Bundle\CustomBundle\EventSubscriber\ProductSaveSubscriber
services:
vendor.bundle.event_subscriber.product_save:
class: '%vendor.bundle.event_subscriber.product_save.class%'
arguments:
- '#pim_catalog.repository.product'
tags:
- { name: kernel.event_listener, event: akeneo.storage.pre_save, method: onPreSave, priority: 255 }
In ProductSaveSubscriber.php:
/**
* #var ProductRepositoryInterface
*/
protected $productRepository;
public function __construct(ProductRepositoryInterface $productRepository)
{
$this->productRepository = $productRepository;
}
public function onPreSave(GenericEvent $event)
{
/** #var Product $subject */
$subject = $event->getSubject();
if ($subject instanceof Product) {
$originalProduct = $this->productRepository->findOneByIdentifier($subject->getIdentifier());
foreach ($subject->getAttributes() as $attribute) {
if ($attribute->getReadOnly()) {
echo "{$attribute->getCode()} : {$subject->getValue($attribute->getCode())}\n";
echo "{$attribute->getCode()} : {$originalProduct->getValue($attribute->getCode())}\n";
}
}
}
}
Now when I run this code, I expect the second echo-statement to give the original data (since I've loaded that anew). However, the original product I load from the repository also has the new data.
Another thing to note here is that if I add a die()-statement, the data is not stored in the database. So it seems that the repository returns the in-memory model or something like that.
Can anyone point me in the right direction? Or am I using the wrong approach to compare newly-entered data with already existing data?
Now when I run this code, I expect the second echo-statement to give
the original data (since I've loaded that anew). However, the original
product I load from the repository also has the new data.
This might be because the object is referenced in doctrine's unit of work. So when you use the repository to fetch what you think is the original object, might actually be the same object you've updated.
Another thing to note here is that if I add a die()-statement, the
data is not stored in the database. So it seems that the repository
returns the in-memory model or something like that.
That's because since your subscriber is listening on the PRE_SAVE event, the updated product has not been flushed in the database yet. Saving a product goes this way:
PRE_SAVE event thrown
COMMIT / FLUSH
POST_SAVE event thrown
So if you call die during the PRE_SAVE event, the COMMIT / FLUSH won't be called.
Can anyone point me in the right direction? Or am I using the wrong
approach to compare newly-entered data with already existing data?
I don't know your particular use case but you might want to use a Query function (see https://github.com/akeneo/pim-community-dev/blob/2.2/src/Pim/Bundle/CatalogBundle/Doctrine/ORM/Query/FindAttributesForFamily.php). It's purpose is to directly fetch in the database the data you need (it will be the original value since the product hasn't been flushed in DB on PRE_SAVE)
I hope this helps.

Symfony - Read-only entities hydrated from YAML

Is it possible to have simple read-only entities, that can have an association with an other doctrine entity, but their data is stored in a text ( YAML ) file ?
Let's say I have a product entity, and I want to set a category for each product. But for now, I only have very few categories ( and don't need to edit or add ), so I don't want/need to create a full doctrine entity with it's own table in the DB.
So I create a very simple entity:
class ProductCategory
{
private $id;
private $name;
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
}
Now I would like to create a simple YAML file where the data is stored, like so:
0: Cheese
1: Meat
2: Dairy Products
....
Then I would like to set a ManyToOne relation from the product entity to the ProductCategory entity.
Is that possible ?
And, how to "query" the categories ? ( let's say I want to list all categories that start with a certain letter )
'Why' you ask ?
Well for now, as I said, I only have very few categories, but maybe some day I want to add many more, and even have a CRUD editor for them and so on, then I could easily convert it to a full doctrine entity.
Or any other suggestions on how to approach this ?
There is already a library that provides what you are looking for that's called Alice:
https://github.com/nelmio/alice
https://github.com/hautelook/AliceBundle
https://github.com/fzaninotto/Faker
https://github.com/h4cc/AliceFixturesBundle
This way you can create random test data en masse and can still work with Doctrine as usual.
If you want to do this manually it will be a pain to solve the problem of connecting the entities. Your best bet is to keep all of them in arrays with id's being used as keys but even then you will probably end up writing lots of glue code to connect the entities.

symfony2 + doctrine2 - how lazy loading affect our applications?

This will be a sort of Q/A for everyone that wonder about symfony2 + doctrine2 and performance.
I'm writing this Q/A because I've started to playing around SF2+doctrine for a new project and I wanted to do some performance tests. I've surfed the net but didn't find a complete answer for my doubts so I've decided to make my own one. Hope this will be useful for someone
Let's say we have two entities like the following
//all ORM declaration here
class Lodging
{
//some declaration here
/**
* #ORM\ManyToOne(targetEntity="Currency")
* #ORM\JoinColumn(name="currency_iso_code", referencedColumnName="iso_code")
*/
protected $currency;
//some methods here
}
//all ORM declaration here
class Currency
{
/**
* #ORM\Id
* #ORM\Column(type="string", length=3)
*/
protected $iso_code;
/**
* #ORM\Column(type="string")
*/
protected $description;
//some methods here
}
Now you will made an entity type for Lodging where you will be able to create or - if the entity passed to the type has some prefetched values - to edit a Lodging object (let's concentrate on that case). In that form you also need the description of Currency, not only the $iso_code.
Question: is better to use ->findOneById() doctrine2 method or to write a DQL (or use query builder facility)?
Why?
PREMABLE
This answer has sense only if you're experiencing a slow page that's using lazy loads. For more common actions (load just one entity, load a "static page" or a cached one and so on) you could continue to use built in function provided by doctrine
Well, let's analyze some of the different methodologies you can follow
1 - findOneById()
Into controller you have chosen to follow more "common" and "fast" way to procede: use pre-existent findOneById() method. Wonderful.
Let's check performance
Number of queries done: three query were done as we need one to retrieve Lodging object, the second one to fetch currently associated Currency (lazy loaded) and one to fetch all Currency entities (because you can change from one currency to other)
Page loading time: about 500ms
Memory usage: about 32MB
2 - Write a custom repository method
2.1 "Basic" DQL repository function
public function findLodgingById($id)
{
$lodging = null;
$q = $this->createQueryBuilder('lodging')
->select('lodging')
->where('lodging.id = :id')
->setParameter('id', $id)
->getQuery();
$lodging_array = $q->getResult(); //will not fetch a single object but array
if ($lodging_array) {
$lodging = reset($lodging_array);
}
return $lodging;
}
Let's check performace
Number of queries done: it shouldn't be a surprise for you but ... number of query done is always three! Of course you're not doing anything but same of findOneById() (an, maybe, even in a worst way!). You're taking advantage of lazy loading again.
Page loading time: about 500ms. Loading time didn't change
Memory usage: about 34MB. Memory usage is increased of 6,25 % (due to array?)
2.2 DQL with JOIN
public function findLodgingById($id)
{
$lodging = null;
$q = $this->createQueryBuilder('lodging')
->select('lodging')
->leftJoin('lodging.currency', 'currency')
->where('lodging.id = :id')
->setParameter('id', $id)
->getQuery();
$lodging_array = $q->getResult(); //will not fetch a single object but array
if ($lodging_array) {
$lodging = reset($lodging_array);
}
return $lodging;
}
Let's check performace
Number of queries done: The number of queries dind't change! But ... why? We are telling explicitly to doctrine2 to join currency entity but it seems to ignore that instruction. The answer is that we're not selecting currency entity also so doctrine2 will use, again, lazy loading facility.
Page loading time: about 500ms. Loading time didn't change 2.1
Memory usage: about 34MB. Memory usage didn't changed from 2.1
2.3 Let's try something better: Join with Currency selection
public function findLodgingById($id)
{
$lodging = null;
$q = $this->createQueryBuilder('lodging')
->select('lodging', 'currency')
->leftJoin('lodging.currency', 'currency')
->where('lodging.id = :id')
->setParameter('id', $id)
->getQuery();
$lodging_array = $q->getResult(); //will not fetch a single object but array
if ($lodging_array) {
$lodging = reset($lodging_array);
}
return $lodging;
}
Let's check performace
Number of queries done: Finally number of queries decreased! We reach two query instead of three. What query gone? Lazy loading of associated (current) currency is gone but, of course, you have to fetch all possible currency.
Page loading time: about 350ms.
Memory usage: about 34MB. Memory usage didn't changed
Definitive Solution (?)
public function findLodgingById($id)
{
$lodging = null;
$q = $this->createQueryBuilder('lodging')
->select('lodging')
->where('lodging.id = :id')
->setParameter('id', $id)
->getQuery();
$q->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true);
$lodging_array = $q->getResult(); //will not fetch a single object but array
if ($lodging_array) {
$lodging = reset($lodging_array);
}
return $lodging;
}
The $q->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true); line of code seems to save time and memory compared to other solutions.
Number of queries done: Two, of course
Page loading time: about 340ms.
Memory usage: about 32MB.
PLEASE PAY ATTENTION
This solution doesn't let you change associated (Currency) entity as it will be betched with Query::HINT_FORCE_PARTIAL_LOAD, true
Comments
Results seems to be good for page loading time (memory usage of course will not change) and though performances seems to be only "a little" better, you shouldn't not focus ONLY onto results: here we're taking as an example only a simple snippet of code with just one lazy loading operation: think about an entity (or, worst, a lot of entity like blog posts with related comments) that will do wrong(*) lazy loading for every entity fetched and managed: you could reach even 50-70 query for a single page (and of course, in this case, you could notice easily performace benefits due to "single" query)
(*) Why I say wrong? Because if you can migrate the fetching-logic of your objects elsewhere or if you already know what entity/attribute you need, so your contents aren't dynamic and you can know them before use, lazy-loading is not only useless but also harmful.
Contrariwise if you can't know at "coding writing time" what properties or entities you'll need, of course lazy loading could save you memory (and time) wasting for useless objects/associations.
Final thoughts
It's better to "lose" some minutes for write DQL query (that seems to be even silly) that use "built-in" ones. Moreover you should use array (and not objects) for read-only operation (list elements that couldn't be modificated) changing getResult() method call as follows: getResult(Doctrine\ORM\Query::HYDRATE_ARRAY);. That will change "default" value (HYDRATE::OBJECT)
It seems like premature optimisation to be worrying about it before you're seeing a problem.
Write whatever's quickest, and in this case, that's often going to be $em->findOneById($id) style. Then use valgrind to look for bottlenecks.
Looking at the amount of time you spend writing all that custom DQL, the overall peformance could be improved by fixing a bigger problem somewhere else in your application.

Symfony/Doctrine entity leads to dirty entity association when merged multiple times with entity manager

I am using Symfony2 and Doctrine
I have a doctrine entity which is serialized/unserialized to a session and used in multiple screens. This entity has a number of one to many associations.
The doctrine entity has the following one to many, for example:
class Article {
...
/**
* #ORM\OneToMany(targetEntity="image", mappedBy="article", cascade= {"merge","detach","persist"})
*/
protected $images;
public function __construct()
{
$this->images = new ArrayCollection();
}
.....
}
The article entity is saved and retrieved as follows:
public function saveArticle($article)
{
$articleSerialized = serialize($article);
$this->session->set('currentArticle',$articleSerialized);
}
public function getArticle()
{
$articleSerialized = $this->session->get('currentArticle');
$article = unserialize($articleSerialized);
$article = $this->em->merge($article);
return $article;
}
I am able to save and load the entity to and from the session any number of times, and then merge it back to the entity manager and save. This is only if it is a new entity.
However, once I load an entity from the db and then save it to session, I get problems.
I know, from other posts, that after you unserialise a saved entity, you have to run $em->merge($entity);
I am able to merge the entity, add a new sub-entity (one to many) and then save:
$article = $this->getArticle(); //Declared above, gets article from session
$image = new Image();
$image->setFilename('image.jpeg');
$article->addImage($image);
$this->saveArticle($article); //Declared above, returns the article to session
However, after the first merge and image add, I can't add any more sub-entities. If i try to add a second image, It returns the following error:
A managed+dirty entity <<namespace of entity>>
image#0000000067078d7400000000221d7e02 can not
be scheduled for insertion.
So in summary, i can make any number of changes to an entity and save it to session, but if I run $em->merge more than once while adding sub-entities, the new sub-entities are marked as dirty.
Does anybody know why an entity would be marked as dirty? Do I need to reset the entity itself, and if so, how could I do that?
Got it.
For anyone who might run into this problem in the future:
You cannot merge an entity which has unpersisted sub-entities. They become marked as dirty.
I.E
You may have an article with two images already saved to DB.
ARTICLE (ID 1) -> IMAGE (ID 1)
-> IMAGE (ID 2)
If you save serialise the article to session and then unserialize and merge it. It's ok.
If you add a new image, then serialize it to session you will have problems. This is because you cannot merge an unpersisted entity.
ARTICLE (ID 1) -> IMAGE (ID 1)
-> IMAGE (ID 2)
-> IMAGE (NOT YET PERSISTED)
What I had to do was:
After I unserialize the article, I remove the unpersisted images and store them in a temporary array (I check for ID). THEN i merge the article and re-add the unpersisted image(s).
$article = unserialize($this->session->get('currentArticle'));
$tempImageList = array();
foreach($article->getImages() as $image)
{
if(!$image->getId()) //If image is new, move it to a temporary array
{
$tempImageList[] = $image;
$article->removeImage($image);
}
}
$plug = $this->em->merge($article); //It is now safe to merge the entity
foreach($tempImageList as $image)
{
$article->addImage($image); //Add the image back into the newly merged plug
}
return $article;
I can then add more images if need be, and repeat the process until I finally persist the article back to DB.
This is handy to know in the event you need to do a multiple pages creation process or adding images via AJAX.

Doctrine Fixtures: getOrder() with Join Tables

How do you control the order of fixture loading when two fixtures depend on each other to load?
My schema has a Book entity, an Author entity, and a BookAuthor entity. Books can have many authors, and BookAuthor is required because it has an additional field called 'main' indicating whether the author is the main author of the book.
In BookFixtures, I reference BookAuthorFixtures:
$book1->addBookAuthor($manager->merge($this->getReference('book-author-1')));
In BookAuthorFixtures, I reference BookFixtures:
$bookAuthor1->setBook($manager->merge($this->getReference('book-1')));
One of the fixtures must be loaded first, which means the fixture it references is an undefined index.
Is there a trick to solving this issue?
This question addresses almost the same issue, but the configuration is different and the issue went unresolved despite the accepted answer.
I don't know exactly how did you set up your relationships, but I think that this is what you should do:
1) Load the Author fixtures (they should require any books to be created)
2) Load the Book Fixtures. While you are creating the fixtures, in your load() method, you can create the "BookAuthor" entity for each book, and the book entity should be configured to save the BookAuthor entities it holds when it's persisted. The property "book_authors" inside the book entity could be something similar to this:
/**
* #ORM\OneToMany(targetEntity="your\bundle\Entity\BookAuthor",mappedBy="book", cascade={"persist", "remove"})
*/
protected $book_authors;
So in the Book fixtures, the code snippet that creates a book might be (don't forget to set any extra data, it's just a sample):
// [...]
$book = new Book();
$book->setTitle( 'xxx' )
// Set more data...
$book_author = new BookAuthor();
$book_author->setAuthor( $manager->merge($this->getReference('author-1')) );
$book_author->setBook( $book );
$book->addBookAuthor( $book_author );
$em->persist( $book );
// [...]
I hope it helped!

Resources