Symfony Doctrine order oneToMany by specific order - symfony

I use Symfony 4, and I have a OneToMany relation. I want to order this relation by a specific order. For now, I order only by ASC :
/**
* #ORM\OneToMany(targetEntity="App\Entity\Ingredients", mappedBy="product", fetch="EAGER")
* #ORM\OrderBy({"family" = "ASC"})
*/
private $ingredients;
It works perfectly.
Now I would like to order by a specific order : "vegetables", "fruits" then "meat".
I tried :
/**
* #ORM\OneToMany(targetEntity="App\Entity\Ingredients", mappedBy="product", fetch="EAGER")
* #ORM\OrderBy({"family" = "vegetables, fruits, meat"})
*/
private $ingredients;
Which is of course doesn't work. Still trying, but is there an easy way to achieve it ?

As noted in https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/annotations-reference.html#annref_orderby
The DQL Snippet in OrderBy is only allowed to consist of unqualified, unquoted field names and of an optional ASC/DESC positional statement. Multiple Fields are separated by a comma (,). The referenced field names have to exist on the targetEntity class of the #ManyToMany or #OneToMany annotation.
However if you use DQL you should be able to accomplish this with something like:
$qb = $em->getRepository(Ingredients::class)->createQueryBuilder('i');
$results = $qb->orderBy('FIELD(family,vegetables,fruits,meat)')
->getQuery()
->getResults();
** I havent tested this code **

Related

Symfony entities without relational

I work with Symfony2 and Doctrine and I have a question regarding entities.
In a performance worries, I'm wondering if it is possible to use an entity without going all the associations?
Currently, I have not found another way to create a model inheriting the class with associations and associations specify NULL in the class that inherits.
thank you in advance
OK, a little detail, it's for a API REST (JSON).
This is my class :
/**
* Offerequipment
*
* #ORM\Table(name="offer_equipment")
* #ORM\Entity(repositoryClass="Charlotte\OfferBundle\Repository\Offerequipment")
*/
class Offerequipment
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Charlotte\OfferBundle\Entity\Offer")
* #ORM\JoinColumn(name="offer_id", referencedColumnName="id")
*/
private $offer;
/**
* #ORM\ManyToOne(targetEntity="Charlotte\ProductBundle\Entity\Equipment")
* #ORM\JoinColumn(name="equipment_id", referencedColumnName="id")
*/
private $equipment;
/**
* #VirtualProperty
*
* #return String
*/
public function getExample()
{
return $something;
}
and with QueryBuilder method, i can't get my virtual properties or getters.
Thanks for your help :)
Look at Serialization.
By serialising your entities, you can choose to exclude or expose a property of an entity when you render it.
Look at the Symfony built-in Serializer and/or JMSSerializer.
Otherwise, you can use QueryBuilder and DQL to choose what fields you want to fetch in your queries.
Like this, you can make your own find method in the Repository of your entities.
// src/AcmeBundle/Repository/FooRepository
class FooRepository extends \Doctrine\ORM\EntityRepository
// ...
public function find($id) {
$queryBuilder = $this->createQueryBuilder('e')
->select('e.fieldA', 'e.fieldB') // selected fields
->where('e.id = :id') // where statement on 'id'
->setParameter('id', $id);
$query = $queryBuilder->getQuery();
$result = $query->getResult();
}
// ...
}
Don't forget define the Repository in the corresponding Entity.
/**
* Foo.
*
* #ORM\Entity(repositoryClass="AcmeBundle\Repository\FooRepository")
*/
class Foo
{
// ...
}
By default Doctrine will not automatically fetch all of the associations in your entities unless you specifically each association as EAGER or unless you are using a OneToOne association. So if you are looking to eliminate JOINs, you can just use Doctrine in its default state and it won't JOIN anything automatically.
However, you this will not alleviate all of your performance concerns. Say, for example, you are displaying a list of 50 products in your application on a single page and you want to show their possible discounts, where discounts are an association on your product entity. Doctrine will create 50 additional queries just to retrieve the discount data unless you explicitly join the discount entity in your query.
Essentially, the Symfony profiler will be your friend and show you when you should be joining entities on your query - don't just think that because you aren't joining associations automatically that your performance will always be better.
Finally, after many days, I've found the solution to select only one entity.
VirtualProperties are found :)
public function findAllByOffer($parameters)
{
$queryBuilder = $this->createQueryBuilder('oe');
$queryBuilder->select('oe, equipment');
$queryBuilder->join('oe.equipment', 'equipment');
$result = $queryBuilder->getQuery()->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true)->getResult();
return $result;
}

Use Doctrine to merge entity with different subclass

I have a mapped superclass AbstractQuestion with single-table-inheritance.
/**
* #ORM\Entity
* #ORM\MappedSuperclass
* #ORM\Table(name="Question")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="dtype", type="string")
* #ORM\DiscriminatorMap({
* "simple": "SimpleQuestion",
* "dropdown": "DropdownQuestion"
* })
*/
abstract class AbstractQuestion
SimpleQuestion and DropdownQuestion inherit from this superclass.
/**
* Class SimpleQuestion.
* #ORM\Entity()
*/
class SimpleQuestion extends AbstractQuestion
I want to modify an existing SimpleQuestion and make it a DropdownQuestion.
When saving a question, I deserialise and merge the question, which contains an ID and the 'dtype' and other properties.
$dquestion = $this->serial->fromJson($request->getContent(), AbstractQuestion::class);
$question = $this->em->merge($dquestion);
$this->em->flush();
So I submit something like:
{ id: 12, dtype: 'dropdown', 'text': 'What is my favourite animal?'}
After the deserialisation, $dquestion is a DropdownQuestion object as I desired, but after the merge $question is a SimpleQuestion object as it was in the database previously, so any unique properties of DropdownQuestion are lost and the question is saved as a SimpleQuestion. Is there any way to work around this?
You will first have to delete the existing record (SimpleQuestion) and then insert the new record (DropdownQuestion). Type casting is not supported in Doctrine 2.
Note.
You can probably change the discriminator column with a pure SQL query, but this is absolutely not recommended and will for sure give you problems...
Check also the answers here and here since they might be interesting for you.

Doctrine2: order associated entity by property of another entity related to it

Let A, B, C be associated entities. I'm aiming for A->getBs() to return a collection of B ordered by two properties: B.createdAt and B.C.displayOrder.
First works fine, second does not: I've tried "c.displayOrder" = "asc", but this has been unsuccessful (Unrecognized field: c.displayOrder).
Is there any way to order by a property from another related entity?
The other option I could imagine is overriding the function getBs(), but I wouldn't know how to create a custom DQL query in there to accommodate for my special need. I've read that using the EntityManager inside an Entity is bad practice.
A
/**
* #ORM\OneToMany(targetEntity="B", mappedBy="a")
* #ORM\OrderBy({"createdAt" = "asc", "c.displayOrder" = "asc"})
*/
-id
-getBs()
B
/**
* #ORM\ManyToOne(targetEntity="A")
* #ORM\JoinColumn(referencedColumnName="id")
*/
/**
* #ORM\ManyToOne(targetEntity="C")
* #ORM\JoinColumn(referencedColumnName="id")
*/
-id
-createdAt
C
-id
-description
-displayOrder
Using doctrine/orm v2.4.5, symfony-standard-edition 2.3.20
Take a look to these 5 solutions described at question below:
Ordering Doctrine Collection based on associated Entity when it is not possible to use the #orderBy annotation

Doctrine says a field doesn't exist when it does - Symfony2

I have an entity:
...
class UserRole extends Role
{
/**
* #ORM\Column(name="user_role_id", type="integer")
* #ORM\Id()
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
...
But when I try to use user_role_id in a doctrine query I get this error:
[Semantical Error] line 0, col 42 near 'user_role_id': Error: Class
MyApp\Model\UserRole has no field or association named user_role_id
The query I am using is this:
$query = $this->getEntityManager()
->createQuery('SELECT r FROM Model:UserRole AS r WHERE r.user_role_id IN (:roles)')
->setParameter('roles', array_values($roles));
I've definitely got a user_role_id field as I can see it in phpMyAdmin.
Does anyone have any idea why doctrine is not recognising it?
This is doctrine and you want to write a DQL not a SQL, and you need to use the field names as defined in entities not the column names.
So in your query just use r.id instead of r.user_role_id
Take a look at DQL in Doctrine

Symfony2 - One ManyToOne relation on one field referencing two entities

I have an entity which stores "removal requests" to either studios or models. An object (Studio or model can have many requests).
Entity RemovalRequest has a field named : object.
I would like to know if it's possible to do something like this in RemovalRequest entity:
/**
* #ORM\ManyToOne(targetEntity="Project\GestionBundle\Entity\Studio", inversedBy="requests")
* #ORM\ManyToOne(targetEntity="Project\GestionBundle\Entity\Model", inversedBy="requests")
*/
private $object;
I can't find anything about this special case over Internet..
If it's not possible, I'm open to any suggestions you might have !
Do you realy need a new entity to store information about removal? Maybe just add a flag to Studio and Model:
/**
* #ORM\Column(name="is_to_remove", type="boolean")
*/
$isToRemove = false;
If you need RemovalRequest entity you should add two properties fore each type like this:
/**
* #ORM\ManyToOne(targetEntity="Project\GestionBundle\Entity\Model", inversedBy="requests")
*/
$model;
/**
* #ORM\ManyToOne(targetEntity="Project\GestionBundle\Entity\Studio", inversedBy="requests")
*/
$studio;
It is bed idea to store two different classes in one property

Resources