Keep two different versions of an entity in Doctrine ORM - symfony

I'm working on a Symfony4/Doctrine/MySQL project with the following requirement:
Users can create entities (say posts) that are visible in the public frontend only after approval by an admin
When a user edits his post after approval/publication, the changed post needs to be approved again before the changes will become visible in the frontend. But while approval is pending the old approved version of the post must remain visible in the frontend.
This means I have to keep two versions of every "Post" entity: the approved version for the frontend and the work-in-progress version for the backend.
In past projects with similar requirements I tried different approaches to this problem:
Using "Versionable behavior" (this was in the Symfony1/Propel days using sfPropelVersionableBehaviorPlugin). For display in the frontend, if an entity was not approved I had to fetch the previous versions until the latest approved version was found.
Using a second entity/database table "ApprovedPost" with the same field definition as the main "Post" entity. When a post is approved by the admin, it will be copied to the ApprovedPost table. The frontend operates on the ApprovedPost table only.
What is the current best practice to implement such a behavior?

Because I am chellenging this problem right now, I want to share my approach.
The entity is a request for something. Everytime something changes, the changes should be preserved.
The approach:
On every edit action, a new entity row is created.
The entity has an "approved" flag and a createdAt Date field.
A request has a nullable one-to-one self relation (to point to the root/parent entity).
A custom repository is used fo accessing the database.
The typical find and findAll methods are modified: they search for the most recent version (by the createdAt field) that is approved.
Search is done via custom SQL/DQL: (WHERE id = ?1 OR WHERE parent-id = ?1) AND WHERE approved = true SORT BY created_at DESC LIMIT 1
More features can be added like enabled/disabled, deletion and so on ...
If you want to render the page server side and show the changed or old versions, I would recommend writing e.g. a Twig extension. You could implement different search functions in the repo and handle the representaion via the extension (sorting, referencing ...).
I created an API, which is able to do both: return the newest version and return all versions (with or without root or newest), but I use the latter one in the frontend only if necessary.
Like dbrumann stated, this is a opinionated approach (I like custom repos, because I can create them type safe and I can decouple application from persistence logic).

Related

Dynamic Access Control for entities in symfony 4

I try to manage the access rights for users to edit or view different articles.
Articles can be created dynamically and the rights should be editable for every article.
In my case, I have a User object and multiple other objects (Article, and more...).
I need to check if a User can read or write any kind of object.
I actually see there is a method Voters, but they only can manage User groups?
Can somebody help me?
A Voter can decide almost anything - usually it's based on a user's permission, but it doesn't have to be - I've used one as a 'feature flag' check, with a value fetched from a configuration, or database entry to show something - or not, as an example.
The page on voters has an example on viewing, or editing a database record (a Post entity, via a $this->denyAccessUnlessGranted('edit', $post);.
In your instance, the voter would be passed the 'attribute', the object (Article, etc) you want to check on, and gets the current user from a service. If that user has the appropriate permission to read/edit/delete the Article or other object, it returns true.

Avoid doctrine postloadEvent if unnecessary

For an blog entry entity I am loading data from an api via the doctrine postloadEvent. For this i created a listener service with an postloadMethod in it.
public function postLoad(BlogEntry $blogEntry)
{
$blogentry->setName($apiClient->getName($blogEntry->getId()))
$blogentry->setContent($apiClient->getContent($blogEntry->getId()))
...
}
This means, there is already a local repository with blog entries. These blogentries are connected to a blog. If i now only want to count the blogentries for each blog, i would implement a getBlogEntryCount() method on the BlogEntry entity and call it.
$blog->getBlogEntryCount();
The problem now is, that the postLoad event is triggered unnecessary, even if i need no data from the api.
How can i avoid this behaviour in doctrine/symfony/sonata admin? Is there mechanism like "lazy loading" for doctrine entities?
Update to Jose M. González Solution
To get only the count of the collection, the extra_lazy loading solution will do it.
For getting local information without triggering the api call i used the said repository function. To get this information working in list view in sonata admin, i created a non-doctrine-related array field "blogEntriesSimple" in my Blog Entity next to my "blogEntries" (which is normally holding the complete entity) field.
I attached an entitylistener with postLoad function to the Blog Entity, which is filling up my blogEntriesSimple array with the information from my custom repository function.
Thats it.
I think this solution is a bit hacky, but until no cleaner solution is available, this will do it.
i think that you can achieve this with the extra lazy associations that permit that you count your related entities without hydrating it
Edited
Also you can do a DQL query that only hidrate a partial view of your entity and it can be used to count the rows and access to your properties, for example:
select be.id,be.title from AppBundle\Entity\BlogEntry be
This query must not trigger the postLoad event
I hope this can help you

CRM 2015 - Show an alert based on records existing in another identity

I'm working on a project where I've been asked to show an alert in the "Account" form that notifies our users that an active record exists in a custom entity.
From the reading I have done so far I can see that
Xrm.Page.ui.setFormNotification('Message here', 'WARNING') appears to have the exact functionality that I need, but, how do I go about implementing the logic that shows this message. Presumably I need to do a count of associated records in this entity and if it's > 0 then show the alert, but, do I need to do this via a plug-in or is it Jquery? Or, am I vastly overcomplicating the issue when there is OOB functionality that will do this?
Any advice appreciated!
Adam
If your custom entity is a sub grid on the account form you can do this using JavaScript.
var count = Xrm.Page.getControl("custom_grid").getGrid().getTotalRecordCount();
if(count > 0) {
Xrm.Page.ui.setFormNotification('Message here', 'WARNING')
}
If its not a sub grid you will need to perform an API call to count the number of related records, you are best using the Web API, Use Microsoft Dynamics CRM web services.
The standard functionality you have at your disposal is roughly:
Workflows
Plugins
Business Rules
JavaScript
Of these options, only JavaScript currently supports setting form notifications. As a side-note, Business Rules do support showing error messages on specific fields (corresponding to setNotification from JavaScript).
You are thus correct that you would write JavaScript that determines whether the related records exist, and subsequently calls setFormNotification.

symfony dynamically add translation based on condition

I'm searching for a way to add a translation to an existing translation catalogue during runtime.
I have a working symfony 2.3 application which uses translations in de/en/fr/it and fetches all available translation keys from /Resources/translations/messages..yml.
Now if a user logs in I want to have the possibility to override some of the already loaded labels based on setting for that user (e.g. textfield in DB which holds key-value-pairs).
E.g.
messages.en.yml
company.name.short: Company profile
Usersetting:
company.name.short: Profile for company
I found no way to add/override keys to the existing catalogue or to make them available in twig. Is there a Bundle or a setting or some Symfony magic to get this to work?
You'll probably want to extend Symfony's own translation class for this. This article explains how to do that:
http://www.webtipblog.com/extend-symfony-2-translator-to-log-untranslated-messages-to-a-database/
The key point is to override the "translator.class" parameter in your config, and then point it to your own class that first checks for database overrules and will defer to the symfony default implementation if it cannot find one.

Event logging in Symfony2

We would like to keep track of all user specific actions regarding the database, e.g. changing the username, getting assigned to a company and so on.
Is there any Symfony bundle that supports this kind of history logging?
doctrine offers some extensions like timestampable, blameable....
look here : http://symfony.com/en/doc/current/cookbook/doctrine/common_extensions.html
Every time an entity changes, it must have 'updatedBy' 'updatedAt' field....
(edit : excuse, i changed , first link was in french)

Resources