Doctrine Many to Many relations - sql

i edit the thread for put more info.
I have the "User" entity and the "Rol" entity, and i am fighting to do work the collection of Roles of a User.
In the User entity i defined:
/**
* #ManyToMany(targetEntity="AppsManantiales\CommonBundle\Entity\Perfil")
* #JoinTable(name="usuarios_perfiles",
* joinColumns={#JoinColumn(name="idUsuario", referencedColumnName="idusuario")},
* inverseJoinColumns={#JoinColumn(name="idPerfil", referencedColumnName="idperfil")}
* )
*/
protected $perfiles;
And in the constructor:
public function __construct(){
$this->perfiles = new \Doctrine\Common\Collections\ArrayCollection();
$this->contacto = new \Doctrine\Common\Collections\ArrayCollection();
}
Before the class namespace put:
use AppsManantiales\CommonBundle\Entity\Perfil;
When execute:
php app/console generate:doctrine:entities CommonBundle
An error appear:
[Doctrine\Common\Annotations\AnnotationException]
[Semantical Error] The annotation "#ManyToMany" in property AppsManantiales\CommonBundle\Entity\Usuario::$perfiles was never impo
rted. Did you maybe forget to add a "use" statement for this annotation?
Any ideas ?.

First part: So in this case, u got a relation many-to-many between Role entity and User entity. First of all, check, r entities correct after generting. Here u can find examples of establishing different realtions: http://docs.doctrine-project.org/en/latest/reference/association-mapping.html && http://docs.doctrine-project.org/en/2.0.x/reference/association-mapping.html (the second has more information with examples of Doctrine queries)
Second part of your question: after establishing right relations, select query of your User gonna be smth like:
$user = $em->createQueryBuilder()
->select('u, r')
->from('YourBundle:User', 'u')
->innerJoin('u.roles', 'r')
->where('u.id IN (:ids)')
->setParameter('ids', $ids)
->getQuery()
->getResult();
And as u guess, u can get roles with the help of your accessor: $user->getRoles()
p.s. yes, ofcource if all entities r correct, u can add methods manually.
EDITED
Oh sry, I forgot, u use Symfony2. So by default in your entities, u got such line:
use Doctrine\ORM\Mapping as ORM;
As u can notice, all annotations u used were with prefix #ORM\. exmpls:
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
So just add prefix #ORM and the result:
/**
* #ORM\ManyToMany(targetEntity="AppsManantiales\CommonBundle\Entity\Perfil")
* #ORM\JoinTable(name="usuarios_perfiles",
* joinColumns={#ORM\JoinColumn(name="idUsuario", referencedColumnName="idusuario")},
* inverseJoinColumns={#ORM\JoinColumn(name="idPerfil", referencedColumnName="idperfil")}
* )
*/

Related

Doctrine ORM\Table(name="name") not working

My Entity class:
use Doctrine\ORM\Mapping as ORM;
/**
* CustomerEntity
* #ORM\Entity
* #ORM\Table(name="customers")
* #ORM\Table(uniqueConstraints={
* #ORM\UniqueConstraint(name="email", columns={"email"}),
* })
* #ORM\Entity(repositoryClass="Customer\V1\Rest\Customer\CustomerRepository")
*/
class CustomerEntity
{
But when I am adding a customer it throws this error, it is looking for the wrong table.
SQLSTATE[42S02]: Base table or view not found: 1146 Table
'database.customerentity' doesn't exist
I did try with this, but doesn't help:
#ORM\Table(name="`customers`")
Schema generation shows this:
$ doctrine-module orm:schema-tool:update --dump-sql
The following SQL statements will be executed:
CREATE TABLE CustomerEntity (id VARCHAR(36) NOT NULL,
.....
What am I doing wrong?
I have cleared the cache too
orm:clear-cache:metadata
orm:clear-cache:query
orm:clear-cache:result
The problem you have is, that Doctrine expects each Annotation only once. Try this:
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table(name="customers", uniqueConstraints={
* #ORM\UniqueConstraint(name="email", columns={"email"}),
* })
* #ORM\Entity(repositoryClass="Customer\V1\Rest\Customer\CustomerRepository")
*/
class CustomerEntity
{
This is really frustrating, after going through ORM core codes, I was able to pinpoint the problem. Don't have time to investigate further to verify the bug or this being a feature (normal behaviour).
By inserting #ORM\Table(name="customers") below uniqueConstraints, ORM is able to recognize the table name.
use Doctrine\ORM\Mapping as ORM;
/**
* CustomerEntity
* #ORM\Entity
* #ORM\Table(uniqueConstraints={
* #ORM\UniqueConstraint(name="email", columns={"email"}),
* })
* #ORM\Table(name="customers")
* #ORM\Entity(repositoryClass="Customer\V1\Rest\Customer\CustomerRepository")
*/
class CustomerEntity
{

Doctrine ORM, many to many, lazy loading. Retrieve an Entity and fetch join all the feautures

I have Zend Framework 3 + Doctrine ORM application.
Class Goods have link "characters":
/**
* Goods
*
* #ORM\Entity
* #ORM\Table(name="goods")
* #property int $id
*/
class Goods implements InputFilterAwareInterface
{
/**
* #ORM\ManyToMany(targetEntity="\Application\Entity\CharacterValue", inversedBy="goods")
* #ORM\JoinTable(name="character_value_item",
* joinColumns={#ORM\JoinColumn(name="good_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="value_id", referencedColumnName="id")})
**/
protected $characters;
public function getCharacters()
{
return $this->characters;
}
}
I trying to use this method to get characters by method for lazy loading, but it returns just one character. Not all characters for the product.
$dql = 'SELECT u, ch FROM Goods u LEFT JOIN u.characters ch';
This method from here:
$query = $em->createQuery('SELECT u, p FROM CmsUser u JOIN u.phonenumbers p');
$users = $query->getResult(); // array of CmsUser objects with the phonenumbers association loaded
$phonenumbers = $users[0]->getPhonenumbers();
https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/reference/dql-doctrine-query-language.html#dql-select-examples
I do not understand why documentation's method working wrong. What is the right way to deside my issue?

How to Join with Native SQL Query and doctrine

I'm developping an application with symfony 3.4. I want to execute a specific query. So i have two entities: the first is PrPurchaseRequest. the second is PrSpecificFieldValue. PrPurchaseRequest has oneToMany prSpecificFieldValues.
I want to get id of purchaseRequest and prSpecificFieldValues
i did that
$queryBuilder = $this->getEntityManager()->createQuery('select p.id as purchaseId, pr.keyField AS keyField,pr.ID AS prkeyvalueid from '.PrPurchaseRequest::class. ' p LEFT JOIN '. PrSpecificFieldValue::class .' spec ON p.id = spec.purchaseId ');
and that didn't work for me
[Syntax Error] Error: Expected end of string, got
'ON'
how can i do it
Using doctrine you need to play around your entities and their mappings with other entities in order to relate them like
use Doctrine\Common\Collections\ArrayCollection;
/** #Entity */
class PrPurchaseRequest
{
/**
*
* #OneToMany(targetEntity="PrSpecificFieldValue", mappedBy="prPurchaseRequest")
*/
private $prSpecificFieldValues;
// ...
public function __construct() {
$this->prSpecificFieldValues = new ArrayCollection();
}
}
/** #Entity */
class PrSpecificFieldValue
{
/**
*
* #ManyToOne(targetEntity="PrPurchaseRequest", inversedBy="prSpecificFieldValues")
* #JoinColumn(name="pr_purchase_request_id", referencedColumnName="id")
*/
private $prPurchaseRequest;
}
Now you have defined relationship between your entities you can join them based on their mapping (prSpecificFieldValues defined on PrPurchaseRequest class ) like
Its DQL (DQL != SQL)
SELECT p,v
FROM PrPurchaseRequest p
JOIN p.prSpecificFieldValues v
No need to specify ON clause doctrine will handle this for you.
One-To-Many, Bidirectional

Doctrine ManyToOne with join table - filter results

I have a Customer entity and User entity with a JOIN relationship table customers_users.
In the Customer entity I have this.
/**
* #var User
*
* #ORM\ManyToMany(targetEntity="Entity\User")
* #ORM\JoinTable(name="customers_users",
* joinColumns={#ORM\JoinColumn(name="customer_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id", unique=true)}
* )
*
*/
private $users;
I want that when a customer is created I can assign the users to it and actually it works well, however I want filter the users to show only those that don't are assigned to none customer.
For example, I have two customers and two users and the first customer has assigned two users; then when I edit the second customer, two users appear to be assigned to it but when I select these and I send the form, it throws the following exception:
An exception occurred while executing 'INSERT INTO customers_users (customer_id, user_id) VALUES (?, ?)' with params [2, 1]:
SQLSTATE[23505]: Unique violation: 7 ERROR: duplicate key value violates unique constraint "uniq_2763c6cca76ed395"
DETAIL: Key (user_id)=(1) already exists.
/**
* #var User
*
* #ORM\ManyToMany(targetEntity="Entity\User", inversedBy="customers")
* #ORM\JoinTable(name="customers_users",
* joinColumns={#ORM\JoinColumn(name="customer_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id", unique=true)}
* )
*
*/
private $users;
and in the User entity, in customers annotation you should put:
* #ORM\ManyToMany(targetEntity="Entity\Customer", inversedBy="users")
(...)
What I actually did in this situation, is:
// UserAdmin.php that extends SonataAdminBundle's User admin
/**
* {#inheritdoc}
*/
public function createQuery($context = 'list')
{
/** #var QueryBuilder $query */
$query = parent::createQuery();
$request = Request::createFromGlobals();
// Could not find another way to detect that admin list is created from
// sonata_type_model_list parent.
// Of course, this applies only to sonata_type_model_list field type.
if ($request->query->get('pcode') == 'demo.admin.customer') {
$alias = $query->getRootAliases()[0];
$query->leftJoin($alias . '.customers', 'c');
// $query-> ... do filtering here
}
return $query;
}
This way I could filter Users in any way I wanted.

Doctrine2 update many-to-many relations

I hava relations Many-to-Many with Product entity and Feature entity
Product entity:
/**
* #ORM\ManyToMany(targetEntity="Feature")
* #ORM\JoinTable(name="Product_Feature",
* joinColumns={#ORM\JoinColumn(name="Product_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="Feature_id", referencedColumnName="id")}
* )
*/
private $features;
Feature entity:
/**
* #ORM\ManyToMany(targetEntity="Product", mappedBy="features")
* #ORM\OrderBy({"position" = "ASC"})
*/
private $products;
ProductRepository.php:
public function updateFeatures($id, $featuresIds)
{
return $this->getEntityManager()->createQueryBuilder()
->update('TestCatalogBundle:Product', 'p')
->set('p.features', ':features')
->where('p.id = :id')
->setParameter('features', $featuresIds)
->setParameter('id', $id)
->getQuery()
->getResult();
}
But when I call updateFeatures I get error:
features = :features': Error: Invalid PathExpression.
StateFieldPathExpression or SingleValuedAssociationField expected
How can I update Product_Feature table? Also I can't delete all features from Product_Feature by product's id.
I changed my controller in next way:
$em = $this->getDoctrine()->getEntityManager();
$features = $em->getRepository('TestCatalogBundle:Feature')->findBy(array('id' => $featureIds));
$product = $em->getRepository('TestCatalogBundle:Product')->find($id);
$product->getFeatures()->clear();
foreach ($features as $feature) {
$product->addFeature($feature);
}
$em->persist($product);
$em->flush();
But if I use native sql I need 2 queries for deleting features and insert new features. But here I need 2 select queries. Maybe I made this task wrong?
You're doing it the wrong way. You should read this chapter of the documentation: Working with associations. You should add an "inversedBy" keyword in the $features field of the Product class.
When you have a bi-directional many-to-many relation, the usual way to do this is:
$product->getFeatures()->add($feature); // Or $product->setFeatures($features);
$feature->getProducts()->add($product);
$em->persist($product);
$em->persist($feature);
$em->flush();