Set up non-persistent relation in Doctrine 2 - symfony

I have an object $user that has a one to many relation with $establishment. I can use:
$user->getEstablishments();
The user can select a stablishment to work on. I have this method that I call in the controller:
$user->setCurrentEstablishment($establishment);
And this one that I call in the view:
$establishment = $user->getCurrentEstablishment();
I want to be able to call:
$user->setCurrentEstablishmentBy Slug($establishment_slug);
where the slug is a string, and let the user object look for the establishment.
Doctrine discourages the practice of accessing the Entity Manager inside the Entity object, but I think that using it in the controller is even worse.
I suspect that some special Doctrine annotation exists that takes care of non persistent relations like this, or some method other than serving the Entity Manager through a service should be used here. Some easy way of referencing other entities from inside the model.
¿Is there any? ¿How could I do that?

There is no Annotation in Doctrine which could convert slug into object.
What can help You is ParamConverter, with it you can automatically convert slug from query into object. But it still must be used in Controller.
Example usage:
/**
* #Route("/some-route/{slug}")
* #ParamConverter("object", class="AppBundle:Establishment", options={"id" = "slug", "repository_method" = "findEstablishmentBySlug"})
*/
public function slugAction(Establishment $object)
{
...
Docs about param converter: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html

Related

Turn deserialized/denormalized entity into doctrine proxy?

I am persisting some entities to a json based db using the symfony serializer.
When retrieving them from the json db they are deserialized again.
Not all fields are serialized before persisting to the json db.
Is it possible to turn deserialized entities into doctrine proxies, so their relationships can be queried?
Example
App\Entity\Employer and App\Entity\Employee (Employee --ManyToOne--> Employer), Employer is not null on Employee.
The relationship is not serialized before persisting Employee to json db.
$documentFromJsonDb = ...;
/** #var App\Entity\Employee */
$employee = $this->normalizer->denormalize($documentFromJsonDb, 'json');
$employee->getEmployer(); // This returns null :( not making a db call
$employee = MagicMethod($employee);
$employee->getEmployer() // This returns an instance of App\Entity\Employer :)
I had a look into the Doctrine\Common\Proxy\ProxyGenerator so far.
Is there a non hacky way to do this without too much overhead?
Thanks for reading!
I think what you want to do is to merge your decoded entity to be able to use Doctrine abilities ?
📖 Take a look on this documentation of Doctrine : https://www.doctrine-project.org/projects/doctrine-orm/en/2.8/reference/working-with-objects.html#merging-entities
It would be something like :
$documentFromJsonDb = ...;
/** #var App\Entity\Employee */
$employee = $this->normalizer->denormalize($documentFromJsonDb, 'json');
$mergedEmployee = $em->merge($employee);
$mergedEmployee->getEmployer(); // Would this work ? :)
Please tell me if it will do the job ? I did not have time to test it 🤔

What is the best way to create a singleton entity in Symfony 4?

I want to create a settings page, which only has a form in it. If the form is submitted it only updates settings entity but never creates another one. Currently, I achieved this like:
/**
* #param SettingsRepository $settingsRepository
* #return Settings
*/
public function getEntity(SettingsRepository $settingsRepository): Settings
{
$settings = $settingsRepository->find(1);
if($settings == null)
{
$settings = new Settings();
}
return $settings;
}
In SettingsController I call getEntity() method which returns new Settings entity (if the setting were not set yet) or already existing Settings entity (if setting were set at least once).
However my solution is quite ugly and it has hardcoded entity id "1", so I'm looking for a better solution.
Settings controller:
public function index(
Request $request,
SettingsRepository $settingsRepository,
FlashBagInterface $flashBag,
TranslatorInterface $translator,
SettingsService $settingsService
): Response
{
// getEntity() method above
$settings = $settingsService->getEntity($settingsRepository);
$settingsForm = $this->createForm(SettingsType::class, $settings);
$settingsForm->handleRequest($request);
if ($settingsForm->isSubmitted() && $settingsForm->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($settings);
$em->flush();
return $this->redirectToRoute('app_admin_settings_index');
}
return $this->render(
'admin/settings/index.html.twig',
[
'settings_form' => $settingsForm->createView(),
]
);
}
You could use Doctrine Embeddables here.
Settings, strictly speaking, should not be mapped to entities, since they are not identifiable, nor meant to be. That is, of course, a matter of debate. Really, a Settings object is more of a value object than an entity. Read here for more info.
So, in cases like these better than having a one to one relationship and all that fuzz, you probably will be fine with a simple Value Object called settings, that will be mapped to the database as a Doctrine Embeddable.
You can make this object a singleton by creating instances of it only in factory methods, making the constructor private, preventing cloning and all that. Usually, it is enough only making it immutable, meaning, no behavior can alter it's state. If you need to mutate it, then the method responsible for that should create a new instance of it.
You can have a a method like this Settings::createFromArray() and antoher called Settings::createDefaults() that you will use when you new up an entity: always default config.
Then, the setSettings method on your entity receieves only a settings object as an argument.
If you don't like inmutablity, you can also make setter methods for the Settings object.

One-to-many relationship: how to set default entity-value?

I have a one-to-many relationship Cart-SendingMethod. I would like to set a default SendingMethod for new Carts. So I have tried this:
<?php
/**
* #ORM\ManyToOne(targetEntity="MetodoEnvio", inversedBy="metodoEnvios")
* #ORM\JoinColumn(name="metodo_envio_id", referencedColumnName="id")
**/
private $metodoEnvio = 1;
but doesn't work... I get:
Impossible to access an attribute ("id") on a integer variable ("1") when I call Cart.SendingMethod.id from a view file
So how to set a default SendingMethod for new Products?
I could do it in the controller, but I would like to know if it is possible from the entity Product.
Note: I didn't know exactly if this is a symfony or doctrine question.
You don't want to introduce dependencies into your entity.
The obvious and cleaner way to do it would be to create a CartFactory service, and inject that into any controller (or other class) that needs to create carts. Inject your EntityManager and other dependencies into the factory. That way you DRY up your cart-initialization code, and avoid bulking up your controller.
Just set the property's default value inside the constructor like this:
public function __construct(..)
{
$this->property = new OtherObject();
}

Creating a new entity in Symfony2 with Doctrine by using the "namespace"

You know that in Symfony2 a new entity can be defined as in the following example:
use Acme\StoreBundle\Entity\Product;
public function defaultController() {
$product = new Product();
$product->setName('Pippo');
$product->setPrice(19.99);
....
// Use Doctrine EntityManager to store the Product object
}
Suppose that you know that the Product class has the following namespace: "AcmeHomeBundle:Product". It would by nice to create the $product object by using the namespace (e.g. by using the EntityManager or something similar).
public function defaultController() {
$item = createObjectFromNamespace("AcmeHomeBundle:Product");
$item->setName('Pippo');
$item->setPrice(19.99);
....
// Use Doctrine EntityManager to store the Item object
}
Do you know if this is possible?
Suppose that you have a string that provides the entity type
You should do this...
$entityInfo = $this->em->getClassMetadata("entityNameSpace:entityName");
$entityMember = $entityInfo->newInstance();
If you wanna use a setter method by string:
$entitySetMethod = "set".\ucfirst("entityDataMemberName");
\call_user_func(array($entityMember, $entitySetMethod), $parameter);
If you really want to, you can do this:
$product = new Acme\JournalBundle\Entity\Product();
$article = new Acme\JournalBundle\Entity\Article();
But you'd have to type it out every time you wanted to create a new entity in that namespace. If you simply used a use statement at the top of you class:
use Acme\JournalBundle\Entity\Product,
Acme\JournalBundle\Entity\Article;
You could then create new articles and products with a simple:
$product = new Product();
$article = new Article();
They do the same thing.
Acme\StoreBundle\Entity\Product IS the namespace of your entity. AcmeStoreBundle:Product is just an alias for the namespace to be used in DQL as a shorter alternative to the real namespace.
Why would you want to create objects with aliased namespace? I suppose you could create some kind of a factory using alias to map it to a real namespace, create an object and return it. But what's the point?
Entity aliases are defined via Configuration: http://www.doctrine-project.org/api/orm/2.2/source-class-Doctrine.ORM.Configuration.html#153
you can not only set them but also retrieve, so if you really need this functionality you should be able to do this with Configuration instance.
It's hard to find anything about entity aliases in Doctrine docs. Symfony docs explain the purpose of it a little:
alias - Doctrine offers a way to alias entity namespaces to simpler, shorter names to be used in DQL queries or for Repository access. When using a bundle the alias defaults to the bundle name.

symfony2 setter for related entity id

My relation in entity:
/**
* #ORM\ManyToOne(targetEntity="Group")
*/
protected $group;
now I have a setter method setGroup() to set related entity, but there seems no method setGroupId() to set group_id without the entity object. How can I set group_id directly?
I suggest you have a look at Doctrine EntityManager ->getReference() method.
$user->setGroup($em->getReference('Group', 10));
Use a custom repository to create a specific method that will fetch the group, and then set it with setGroup.
Edit: You can even directly add/update the id via a SQL query: https://stackoverflow.com/a/10215061
But that's dirty.
Try defining another field (groupId) and map it directly ot your field in the database.

Resources