Create something like SQL Trigger in Doctrine / Symfony 3 - php

I have a little problem and I can't solve it.
I have a controller, which accept 4 variables using AJAX and I need to insert data in first table and then get ID value from first table and insert it to 2 with 2 additional parameters.
So, my app structure is:
1) Keywords Table with fields keywordId, KeywordVal and Page ID (getted from AJAX)
2) Translations table with fields keywordID (get from Keywords.keywordId), langCode and translation (AJAX)
3) Controller which get data from ajax, proceed it and insert into table.
So, my question is next: how can I configure my EventListener? This listener must be run after flush() method and insret data into Translations table.

Why not you create it all in the controller, because to do all in a EventListener you must in some way to pass the values to the listener.
If you do it in your controller yo can to persist first the first entity with the needed parameters and in after persist the second entity related to de previusly created entity.
Something like:
$em = $this->getDoctrine()->getManager();
$keyword = new Keyword($param1, $param2);
$em->persist($keyword);
$keywordTranslation = new KeywordTranslation($keyword, $param3, $param4);
$em->persist($keywordTranslation);
$em->flush();
I think is so much easy to do

What you seem to need is a Doctrine Listener. Here is the documentation to create a Doctrine listener. You might want to use the preFlush event in my opinion. Be careful, this event is fired for every flush, not only for Keywords, so you've got to check first it is a Keyword before creating Translations.
EDIT: Nevermind, just noticed this does not answer to your question. However, I feel your model could be improved, because you should not have to flush several times to insert a single set of data. Theoretically, you should have a OneToMany relationship between Keywords and Translations, and Doctrine would manage alone to link the two entities with their id at insertion.

Related

Can't get my old Entity in preUpdate Sonata

Here's the deal, i'm trying to update an Entity, but i'd like to compare the state of my entity before the update with the state of my entity after the update.
I try to compare the number of Users in my entity.
For example, if i add a User, i want my code to know that, for example, i had 4 Users before update, and 5 after (and get that User for further use).
After reading different topics, i've tried doing like that in my preUpdate($object){} method
$em = this->getConfigurationPool()->getContainer()->get('doctrine')->getManager();
$original = $em->getUnitOfWork()->getOriginalEntityData($object);
But both
var_dump(count($object->getUsers()));
var_dump(count($original['users']));
Give the same value, and, according to my example, the value is 5 in both case (so the value after the update).
Is there a way i can save the old_state of my entity in a var? What am i doing wrong?
EDIT:
It's not the preUpdate function of Doctrine, but the preUpdate function of SonataAdmin, don't know if they're the same.
Please try this in the preUpdate function:
$em = $this->getModelManager()->getEntityManager($this->getClass());
$original = $em->getUnitOfWork()->getOriginalEntityData($object);

Doctrine2 update relations many to many (without Symfony)

I have 2 Doctrine Entities with many-to-many relations. When I edit the first entity I want to be able to select the checkboxes that have the data from the 2nd entity to establish the joins for particular entry.
It works fine on creating a new Entry (using Array Collection), but when I want to edit an Entry - it adds the ones that I have selected without removing the previous choice (unchecking).
Which way would be the correct way to do that and how?
Remove all the Join table data for the Entry that is being updated,
then set the new data. (How can I remove it from the join table that
is not an Entity?)
Pass all the data from the 2nd Entity and remove
those that aren't checked (seems super-clumsy?)
Some other way I am not aware of?
I am not using Symfony, just Doctrine.
Doctrine makes working with the many-to-many associations quite easy. Your associations are stored into an ArrayCollection class that has some methods that can help you. First of all, check all the available methods for the ArrayCollection here (Doctrine API - ArrayCollection)
In your case, I'd use this approach: use the clear method on your ArrayCollection that contains the relationship with the 2nd entity and populate it again with the checked elements. After this, call the flush method on the entitymanager.
Another approach consists in filtering your collection (with the filter method) for getting a brand new ArrayCollection that contains only the elements that are checked. Like the first approach, associate this new collection to the relationship's ArrayCollection and call the flush method on the entitymanager.

Automated count in doctrine 2

Using Doctrine 2 and Symfony 2.7 I want to use an automated count for a column in my db.
Example:
So when I update a report, I want to add the user (which is the parent of the report by a OneToMany relation) to the leaderboards with the column completed set to 1 (setCompleted). When the user was already on the leaderboards, I want to find him and add 1 to the completed tasks value.
if (!$lb) {
$new = New Leaderboard();
$new->setUsers($user)
->setCompleted('1');
$em->persist($new);
} else {
$update = $em->getRepository('AppBundle:Leaderboard')->findBy(array('user' => $user));
$update->setCompleted('2');
}
So basically I want to automate the $update->setCompleted('2'); so that it takes the current value and adds one to that and then flush that to the database.
I hope this makes any sense? No sure how to explain it or search for it online...
You could use an onFlush listener to watch your Report entity for updates and increment/create the leaderboard entry depending on the state of the Report.
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/events.html#onflush
Requires a bit of (careful) reading, but it will do what you want.
Here's a couple of questions I answered on more or less the same subject with examples that should get you started pretty quick
Track field changes on Doctrine entity
Persisting other entities inside preUpdate of Doctrine Entity Listener
The basic flow you want is to capture updates to Report (the simpler second example under my answer shows this being done for an entity). Then based on the state of Report, you create a new Leaderboard entity and attach it to Report, OR update the existing Leaderboard entity for that report.
Note it's important to not flush the entity, just add it to the UOW like so
$this->getEntityManager()->persist($entity);
$metaData = $this->getEntityManager()->getClassMetadata($className);
$this->getUnitOfWork()->computeChangeSet($metaData, $entity);
Hope that helps!

Doctrine isDeletable method in entity

I have a Entity in my database (say Member) which has many relationships with other tables (6 relationships to be exact). Some of them I don't want mapped with the ORM (I mean linked to this Entity) because they may have many records (like MemberAccessLogs for example) and some other load many other entities.
Now I want this Member Entity to have an isDeletable method so I can disable exclude button in administration page.
If I where to do this the traditional way, I would have to declare the associations with all the other tables in the entity class, including MemberAccessLogs and I would put the method in it so I could test if these associations are empty.
But AFAIU, I would have to make a fetch (or at least a count) to the association's tables in order check for empty.
Another way would be to fetch the Members I want shown and then make a separate query to check for empty with a low cost exists(select * from table limit 1) in these sub-tables and then populate the isDeletable method in Member programmatically before pass it to Twig.
But I found this solution cumbersome. Anyone has a better way to do this ?
Just for the record: Some people may think this is "premature optimization". I maintain (contrary to some), that you should think ahead when you are programming and don't this this is bad. But I really think this isn't the place to discuss it. Please let's focus on the question asked ok ? :)
Edit
To easily prove that limit 1 is increadibly faster than count, I did a small test in a table in my database that has more than 20 million lines. Here are the results:
select count(*) from loga [20 million+ table]
20678473
1 row(s) fetched - 27023ms
select exists(select null from loga limit 1)
true
1 row(s) fetched - 2ms
I guess 13511,5 times faster is conclusive enough. :D
Extra lazy
You could look into extra-lazy associations.
Basically you map all associations as you normally would, and add fetch="EXTRA_LAZY":
/**
* #Entity
*/
class CmsGroup
{
/**
* #ManyToMany(targetEntity="CmsUser", mappedBy="groups", fetch="EXTRA_LAZY")
*/
public $users;
}
Now Doctrine will not load the complete collection into memory the first time it's accessed, but performs specialized queries to load the parts you actually need at that moment.
So $users->count() (or count($users)) on the collection would trigger a simple count-query in stead of loading the complete collection into memory.
PostLoad
You could use an postLoad event to determine if such an entity is deletable. This postLoad event is called after an entity is constructed by the EntityManager, so when the entity is loaded.
Add an unmapped property ($isDeletable) to the entity that stores whether the entity can be deleted or not.
Create an entity listener that listens to the postLoad event. The listener can have the EntityManager, DBAL Connection, or anything else injected. With that dependency you could perform whatever query you want and use the result to set $isDeletable.
The result is a single additional query when the entity is loaded, after which the entity "knows" whether it's deletable or not.
An example of using the postLoad event can found in a Cookbook entry on the Strategy Pattern
Do note that when the conditions that determine whether it's deletable or not change, the value of $isDeletable could become incorrect. To resolve this issue, you could keep track of those conditions:
Keep track
Add a mapped property ($isDeletable) to the entity that stores whether the entity can be deleted or not. It would probably start with true.
When something is added to an association which would mean that the entity is no longer deletable, set $isDeletable to false.
When something is removed from an association that which would mean that the entity is deletable again, set $isDeletable to true.
In other words: with every change you keep track of whether the entity is deletable or not.
This way you won't need any additional queries at all.
There's a Cookbook entry on aggregate fields that explains this concept very well.

Symfony2/Doctrine - Entity Abstraction in Relation to Normal SQL

I am new to Symfony2 and building an app based on an existing legacy MySQL schema. I've become familiar with all the Intro docs (The Book etc) but still needing to inderstand some higher level concepts of how to properly use the framework. Trying to get my head around the concept of an entity in terms of how I normally would go about writing SQL queries. I've used the CLI to generate entities for all my existing tables. As an example ... there is a Clients and a Titles entity already. Titles are 'owned by' Clients and the core Symfony annotations have mapped them correctly.
So, given a titles table with many columns of values but only one titles.client_id ... say I want to create a form action in the ClientsController (clients.yml route: /clients/{id}/add_title) that for the given client id in the url will allow the user to enter a title name and have it save a new record into titles with only the titles.name & titles.client_id values ... very simple really.
My question is ... in defining this very simple query (in normal SQL)
INSERT INTO (titles) VALUES (name, client_id)
DO I need to create another entity for titles JUST to work with those 2 specific values?
OR
What is the ideal way to use part of an entity for a specific repository ... in this case just a subset of the titles table (name & client_id)?
Here is the Action method in my Clients Controller:
//use Entity & Form namespaces for BOTH tables;
public function addTitleAction(Request $request)
{
$client_entity = new Clients;
$titles_entity = new Titles;
// generate simple 2 input form with Form\TitlesType
return etc ...
}
You may be able to tell, I also need to figure out how to work with the Form classes but my basic question here is how to generate simple queries from larger Entities and how to call from the Controllers of another Entity/Table Controller. Thx for your help.
To wrap your head around the new concepts, think of an entity as a row returned from your table. Think of a repository as your queries on the table. So you should have a Title entity (not Titles).
INSERT INTO (titles) VALUES (name, client_id)
DO I need to create another entity for titles JUST to work with those
2 specific values?
You'll want to create a new object when creating a new record (think of the new object as a new record that you then save), along the lines of:
$title = new Title();
$title->setClient($client);
$em->persist($title);

Categories