Issue with combining #ORM\ManyToOne annotation with #ORM\JoinColumn anntation on same variable - symfony

Okay to describe the question I've been attempting to make a join column using doctrine annotations in order to save the username of a user instead of the user_id. I thought to achieve that by using #ORM\JoinColumn and #ORM\ManyToOne annotation but sadly it didn't work and I can't find a suitable way of doing it.
/**
* #Gedmo\Blameable(on="create")
* #ORM\ManyToOne(targetEntity=User::class)
* #ORM\JoinColumn(name="username", referencedColumnName="Username", nullable=false)
*/
private $created_by;
As you can see I combine this logic together with the #Gedmo\Blameable annotation in order to automate user action tracking to see who created the record in the table.
So my question is does somebody have experience with this usecase and what did you do to resolve this problem. I think I'm on the right track but can't find anything that resolves this issue.
I'm also thinking that I found an error in the Symfony Doctrine code because it passes the persistent check. As you can see in the image EnttiyManager->flush is passed. Sorry in advance don't have the right to embed the image.
Errror Screen Symfony
Meaning that this code piece succcesfully goes through the persist and reaches the flush. Changed the url to google because of privacy concerns once again.
$betaallink = new Betaallink();
$betaallink->setDepartment($department);
$betaallink->setOwnreference($data["referentie"]);
$betaallink->setActive(true);
$betaallink->setReturnUrl("https://google.nl");
$entityManager->persist($betaallink);
$entityManager->flush();
And this is the database table for "Betaallink" which means in english "Paymentlink" in order to show that this column indeed exists. Once again no right to embed.
DB Columns
I normally would if this was a personal project include the User table but because of specific column names I can't share it because of privacy concerns.
Sorry in advance for grammar and other langauge faults, I normally don't type or speak fluent English

Related

Linking several entities within an API

Edit (2018/02/18): Since I'm freshly subscribed on SO, if my question is unclear, or if there is a problem with formatting, please let me know.
I have a problem while creating a REST API with Symfony4 and API-Platform.
Things that work :
There are three entities in my Symfony project :
Recipe (with cooking time, steps, etc)
Ingredient (with description, photo, etc)
LinkIR (which contains a link to a Recipe, one to an Ingredient, and a quantity needed for said Ingredient in said Recipe)
When creating my API with API-Platform, I manage to create basic CRUD operations for every Entity.
Here begins my problem :
When I land on my "Pasta" page (Ingredient), I'd like to display every Recipe using Pasta (and the quantity you need for this Recipe). I could do it by calling :
One my Ingredient method to get my Pasta data
N times LinkIR method to get a list of Recipe.id (and quantities)
N times Recipe method to get all the Recipe data
This could work, but it would be slow, not reliable and it will overload my API server quickly.
Is there any way to display my whole page (1 Ingredient + N Links + N Recipes) while calling my API only once ? I began searching in the direction of API-Platform's Subresources, but without result so far.
You can use the Group annotation for the collection member you want to include. For example if Ingredient has a Doctrine relation to Recipe you can tag that relation with a Group name. Please notice that you need to add the annotation to all the members you want to be visible in you response.
/**
* #Group("read")
*/
private $recipes;
Additionally you need to tell the serialization process to utilize that group.
/*
* #ApiResource(attributes={
* "normalization_context"={"groups"={"read"}}
* })
*/
class Ingredient {
You should be able to do the same from "other direction".
https://api-platform.com/docs/core/serialization
Also think about the right level of doing it in one request. Fetching larger doctrine collections and hydrating them will end up slow. A good solution might be to get all the Recipes for a certain ingredient in one request. That's just a normal collection get with a filter which says "all recipes for ingedient x".
https://api-platform.com/docs/core/filters

Why does Doctrine return me an associative array with a gap in the keys when fetching?

I am building an app with Symfony.
I've got a Doctrine entity that contains (among other properties) a collection of another Entity (forming a OneToMany association).
Class OuterEntity
{
/**
* #ORM\OneToMany(targetEntity="InnerEntity", mappedBy="outer", cascade={"persist", "remove"})
*/
private $inners;
}
In my OuterEntityController, in the updateOuterEntityAction method, i do the following :
parse the request
get the updated Outer from db
modify it according to the request
call flush() on the entity manager
call findAll() on the OuterEntity repository
return the OuterEntity list to the client in a JsonResponse in order to notify it of the new state of the db. Since I use a JsonResponse, I let Symfony do the serialization (very probably with json_encode).
Everything is allright except when I delete one or more InnerEntity in the association. The issue does not come from the removal, but from the returned json format. Most of the times, the inners I get are under the form of a simple array:
{"inners":[{inner1},{inner2},...]}
, which is fine for me. But after a removal from this association (in the example, I assume the 2nd one was deleted), I get an array like this:
{"inners":{"0":{inner1},"2":{inner2},"3":{...},...}
Furthermore, this issue doesn't happen if the last Inner is deleted (or several Inners all located at the end of the array).
My supposition is that Doctrine places the association in an associative array and when json_encode serializes this array, it renders in the first format if the indexation is normal (0, 1, 2...), or in the second format if the indexation is broken (0, 2, 3...).
So my question is now : why doctrine does not place the result in a "normally indexed" array after a removal?
I think there's something going on with Doctrine's caching mechanism, but I can't figure exactly what. Maybe it's because the entity manager still considers the deleted entities. But I thought that the entity manager was cleared after the flush, isn't it?
I tried to call clear() on the entity manager, but I've had some strange behaviour and quickly gave up.
I'd be glad if one of you could point the mistake I am making.
It seems that the problem is on my side of the keyboard.
I don't know why, but I retried to clear the entity manager today, and everything works just fine as I first expected. I must have made a really dumb move at my first try.
Thanks everybody for the help, and sorry for the inconvenience.

Polymorphic attachments using a subset of the polymorphic relationship in doctrine 2

I need some help with doctrine 2 that uses "polymorphic associations". Let me clarify myself. Entitys can support file attachments using a subset of the polymorphic relationship. the File entity is used to safekeep this relationship where reference to the files are stored as records in the files table and have a polymorphic relation to the parent model. I want to create the same functionality as https://octobercms.com/docs/database/attachments
But do not know how to make the relationship, and how, for example, put the attachment_type dynamic like attachment_id;
/**
* #var \Doctrine\Common\Collections\ArrayCollection
*
* #ORM\OneToOne(targetEntity="App\Domain\FileAttachment\Entity\FileAttachment", attachment_type="news_thumbnail")
*/
private $thumbnail;
/**
* #var \Doctrine\Common\Collections\ArrayCollection
*
* #ORM\OneToOne(targetEntity="App\Domain\FileAttachment\Entity\FileAttachment", attachment_type="news_image")
*/
private $image;
/**
* #var \Doctrine\Common\Collections\ArrayCollection
*
* #ORM\OneToMany(targetEntity="App\Domain\FileAttachment\Entity\FileAttachment", attachment_type="news_files")
*/
private $files;
An example of the files table.
I have some experience in trying to make polymorphism work (including polymorphic files) in symfony and by this time I think I can share a few of my insights with you in hopes that they would provide you with some useful information about this subject.
Firstly, I would suggest reading up on inheritance mapping in doctrine link. With doctrine inheritance mapping you would simply create one main File class and then make every other attachment extend it. Then, say you want to add a picture attachment to the user. You would simply create a oneToOne relationship between the user and the main File class. If the attachment you persist would be an instance of one of the attachment classes, Doctrine is smart enough to return you an object of that class, not the main File class.
So to answer you question, I will give you a specific example. Case:
ImageAttachment extends FileAttachment
User has a property called photo
Property photo is a OneToOne relationship to the FileAttachment entity
Code:
$image = new ImageAttachment();
$user->setPhoto($image);
$em->persist($user);
$em->flush();
Result:
Now in the database in the User table, in a column called something like photo_id the referenced ID would be the one in the FileAttachment table. When you would do $user->getPhoto(); it would return an object of class ImageAttachment since doctrine knows that you have persisted an ImageAttachment, not just a FileAttachment.
When it comes to collections, things would also be pretty simple. In this case, you would probably need to create an ManyToMany relationship between the file and the entity that you want to relate to the file. Say that a user can have many different types of attachments saved in the database. If you want to use this filesystem application wide it would probably make no sense for a file to know about the user it belongs to, because soon file would have to hold information on all different types of the relationships and that is just not a smart architecture choice if you want to have any type of modular system in place. Thats why my suggestion is to use ManyToMany relationships between some entity and the attachments. This way only user would know about the files in the database and filesystem would be agnostic and decoupled.
A third important point to be made when talking about polymorphism in doctrine is symfony support for this feature. Generally polymorphism is considered to be somewhat of a bad practice in certain cases, and especially in data persistence does not have much support in the community. So an important thing to consider is that symfony CollectionType HAS NO SUPPORT FOR POLYMORPHISM what so ever. Basically you will have to write your own Type if you were planning on using polymorphic form collections. But if you don't mind using a bit of ajax, this is not really a problem, you can simply avoid using SF forms for this purpose alone.

Symfony2 - Doctrine2 store changeset for later (or alternative solution to approve changes)

I have several entities, each with its form type. I want to be able, instead of saving the entity straight away on save, to save a copy of the changes we want to perform and store it in DB.
We'd send a message to the user who can approve the change, who will review the original and the changed field(s) and will approve or not. If approved the entity would be properly flushed.
To solve the issue I was thinking about:
1) doing a persist
2) getting the changesets (both the one related to "normal" fields, and the one relative to collections)
3) storing it in DB
4) Performing $em->refresh() to discard changes.
Later what I need is to get the changset(s) back, ask the (other) user to approve it and flush it.
Is this doable? What I'm especially concerned about is that the entity manager that generated the first changeset is not the same we are going to use to perform the flush, I basically need to "load" a changeset.
Any idea on how to solve the issue (this way, or another way ;) )
Another solution (working only for "normal" fields, not reference ones that come from other entities to the current one, like a many to many) would be to clone the current entity, store it, and then once approved copy the field(s) from the cloned to the original one. But it does not work for all fields (if the previous solution does not work we'd limit the feature just to "normal" fields).
Thank you!
SN
Well, you could just treat the modifications as entities themselves, so that every change is stored in the database, and then all the changes that were approved are executed against the entity.
So, for example, if you have some Books stored in the database, and you want to make sure that all the modifications made to these are approved, just add a model that would contain the changeset that has to be processed, and a handler that would apply these changes:
<?php
class UpdateBookCommand
{
// If you'll store these commands in a database, perhaps this field would be a relation,
// or you could just store the ID
public $bookId;
public $newTitle;
public $newAuthor;
// Perhaps this field should be somehow protected from unauthorized changes
public $isApproved;
}
class UpdateBookHandler
{
private $bookRepository;
private $em;
public function handle(UpdateBookCommand $command)
{
if (!$command->isApproved) {
throw new NotAuthorizedException();
}
$book = $this->bookRepository->find($command->bookId);
$book->setTitle($command->newTitle);
$book->setAuthor($command->newAuthor);
$this->em->persist($book);
$this->em->flush();
}
}
Next, in your controller you would just have to make sure that the commands are somehow stored (in a database or maybe even in a message queue), and the handler gets called when the changesets could possibly get applied.
P.S. Perhaps I could have explained this a bit better, but mostly the inspiration for this solution comes from the CQRS pattern that's explained quite well by Martin Fowler. However, I guess in your case a full-blown CQRS implementation is unnecessary and a simpler solution should work.

create a new entity based on the creation of another (postPersist) Doctrine2

I'm trying to create a new entity based on the creation of another. Example: I have a relationship
Sale <-- 1:M --> Payment
Now, when I persist a Sale, then you must create an initial Payment but i dont know exactly how do it.
i've try:
usage #ORM\HasLifecycleCallbacks(), #ORM\prePersist or #ORM\postPersist, but these methods does not get arguments and i can't persist the Entity Payment. I've even tried to relate Sale with Payment (in prePersist method $payment->setSale($this)) hoping EntityManager to persist Payment for me. info from here
I tried to create a listener (guided from here), but it just does not work, at no time the listener runs
Do it in my SaleController::createAction(), this way is obviously simple and it works, but this is nothing elegant and also goes against the good design of my application, this operation is part of the business logic and repeated in various parts
Out of the 3 solutions you listed 3 is still the least wrong in my opinion. It's simple, not overly complicated and easy to refactor later.
But if you're looking for a clean solution, I think what you need is a form handler or a similar service.
Take a look at FOSUserBundle one.
Basically you will create a PaymentManager class & after handling all the Sales form stuff, pass all the gathered info to PaymentManager and let it handle all the create/persist logic of Payment entity.
I'd suggest that a PaymentManager as suggested by #Inori is the best way to go, it is DRY and also a central point where entities are created. It allows you to marshal all the user input in the controller and then pass it onto the manager to build up the Sale object properly.
If you DO wish to go with the 1st option and use a lifecycle callback I assume you are getting an exception that says an unmanaged entity was found on another entity - or something to that effect. To get around this you can cascade persist on your mapping which means that you don't need to call persist for the Payment:
/**
* One-to-Many via Join Table (aka One-To-Many Unidirectional).
* #ORM\ManyToMany(targetEntity="Payment", cascade={"persist"})
* #ORM\JoinTable(
* inverseJoinColumns={
* #ORM\JoinColumn(unique=true, onDelete="CASCADE")
* }
* )
*/
You can read more about the One-To-One, Unidirectional if it confuses you.
Also you should read about cascade persist Transitive persistence / Cascade Operations.

Resources