I'm having problems writing a correct DQL for my code (Exception : Invalid PathExpression. Must be a StateFieldPathExpression).
I'll start by sketching the structure of my project.
Every entity inherits from IdentifiableObject that provides every entity with an id
/**
* #MappedSuperclass
*/
class IdentifiableObject {
/**
* #Id
* #Column(type="integer")
* #GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* getters and setters go here
*/
}
Then we have a User object that has a list of "UserProperties"
/**
* #Entity
* #Table(name="users")
*/
class User extends IdentifiableObject {
/**
* Other unimportant vars go here
*/
/**
* #Column(type="string")
*/
private $userName;
/**
* #OneToMany(targetEntity="UserProperty", mappedBy="user", cascade={"persist", "remove"}, orphanRemoval=true)
*/
private $userProperties = null;
public function __construct() {
$this->userProperties = new ArrayCollection();
}
/**
* Getters and setters go here
*/
}
The Userproperty has a property (which is basicaly a type saying what kind of userproperty it is) and a value
/**
* #Entity
* #Table(name="userProperties")
*/
class UserProperty extends IdentifiableObject {
/**
* #OneToOne(targetEntity="Property")
* #JoinColumn(name="propertyID", referencedColumnName="id")
*/
private $property;
/**
* #ManyToOne(targetEntity="User")
* #JoinColumn(name="userID", referencedColumnName="id")
*/
private $userValue
}
And finally the Property
/**
* #Entity
* #Table(name="properties")
*/
class Property extends IdentifiableObject {
/**
* #Column(type="string")
*/
private $name;
/**
* #Column(type="string")
*/
private $description;
/**
* #Column(type="integer")
*/
private $mandatory = 0;
/**
* #OneToOne(targetEntity="PropertyType")
* #JoinColumn(name="type", referencedColumnName="id")
*/
private $type;
}
What i would like to do is get all Users that have a Userproperty corresponding to a certain Property.
My attempt was :
$queryUserId = "SELECT userprop.user FROM UserProperty userprop JOIN userprop.property prop WHERE prop.id = '$propertyId'";
or
$queryUserId = "SELECT user FROM User user JOIN user.userProperties userprop WHERE userprop in(SELECT up FROM UserProperty up WHERE up.property = '$property')";
As of today i'm not able to get something sensible out of the system other then "Invalid PathExpression. Must be a StateFieldPathExpression"...
Anyone can help?
Edit: I'll clarify what i'm trying to do here.
What i want is the DQL version of this sql query (which works):
SELECT *
FROM users
WHERE id
IN (
SELECT u.userId
FROM userproperties u, properties p
WHERE u.propertyID = p.id
AND p.id =1
)
Where the p.id = xxx would be the property object (or at least the id) that i want to use in my search.
Ok seems i have found a working DQL solution:
$query = "SELECT user from User user WHERE user.id in(SELECT u FROM UserProperty up JOIN up.user u WHERE up.property = '$property')"
Works! (You need to join on user in the subQuery else you get the dreaded "Invalid PathExpression" exception.
Related
I got this error from my UserRepository:
[Semantical Error] line 0, col 51 near 'h': Error: Class DokMngr\Entity\User has no association named id
I understand that the error comes from the missing declaration inside my user entity. I can not find a correct syntax when a field is both a generated id and a OneToMany association.
Hope somebody can help me to solve my problem or at least explain why I can't use an id as an association.
class UserRepository extends EntityRepository
{
public function findAllwithHistoryCount() {
$qb = $this->createQueryBuilder('u')
->leftJoin('u.id', 'h');
return $qb->getQuery()->getResult();
}
}
/**
* #Entity(repositoryClass="DokMngr\Repository\UserRepository")
* #Table(name="users")
*/
class User implements UserInterface
{
/**
* #Id
* #Column(type="integer")
* #GeneratedValue
*/
protected $id;
}
/**
* #Entity(repositoryClass="DokMngr\Repository\HistoryRepository")
* #Table(name="history")
*/
class History
{
/**
* #Id
* #Column(type="integer")
* #GeneratedValue
*/
protected $id;
/**
* #ManyToOne(targetEntity="User", inversedBy="id")
* #JoinColumn(name="user", referencedColumnName="id")
*/
protected $user;
}
Thanks to Olibiaz I found my error in reasoning.
I thought the whole time that I have to create an association between User.id and History.user like inside the database.
But instead I have to create a new field histories in my User entity. (which exactly not exist inside the database)
So here's the corrected entities:
User
use Doctrine\Common\Collections\ArrayCollection;
/**
* #Entity(repositoryClass="DokMngr\Repository\UserRepository")
* #Table(name="users")
*/
class User implements UserInterface
{
/**
* #Id
* #Column(type="integer")
* #GeneratedValue
*/
protected $id;
/**
* #OneToMany(targetEntity="History", mappedBy="user")
*/
private $histories;
}
History
/**
* #Entity(repositoryClass="DokMngr\Repository\HistoryRepository")
* #Table(name="history")
*/
class History
{
/**
* #Id
* #Column(type="integer")
* #GeneratedValue
*/
protected $id;
/**
* #ManyToOne(targetEntity="User", inversedBy="histories")
* #JoinColumn(name="user", referencedColumnName="id")
*/
protected $user;
}
I'm trying to follow the example from this link to do a many to many relationship with an extra field with Doctrine 2 and Zend Framework 1. My code looks like this:
User Entity
class User
{
/**
* #Id #Column(type="integer")
* #GeneratedValue(strategy="AUTO")
*/
private $user_id;
/**
* #OneToMany(targetEntity="UserChannel", mappedBy="user")
*/
private $userChannel;
/**
* Entity constructor
*/
public function __construct()
{
$this->userChannel = new ArrayCollection();
}
--GETTERS AND SETTERS--
}
Channel Entity
class Channel
{
/**
* #Id #Column(type="integer")
* #GeneratedValue(strategy="AUTO")
*/
private $channel_id;
/**
* #OneToMany(targetEntity="Userchannel", mappedBy="channel")
*/
private $userChannel;
/**
* Entity constructor
*/
public function __construct()
{
$this->userChannel = new ArrayCollection();
}
--GETTERS AND SETTERS--
}
Relational UserChannel Entity:
class UserChannel
{
/**
* #Id #Column(type="integer")
* #GeneratedValue(strategy="AUTO")
*/
private $id;
/** #Column(name="channel_id", type="integer", length=11)
* #ManyToOne(targetEntity="Channel", inversedBy="userChannel")
* #JoinColumn(name="channel_id", referencedColumnName="channel_id")
*/
private $channel;
/** #Column(name="user_id", type="integer", length=11)
* #ManyToOne(targetEntity="User", inversedBy="userChannel")
* #JoinColumn(name="user_id", referencedColumnName="user_id")
*/
private $user;
/**
* Entity constructor
*/
public function __construct()
{
}
--GETTERS AND SETTERS--
}
Now, in my controller I have the following:
$user = $this->em->getRepository('Entities\User')->find(1);
$userChannel = $user->getUserChannel();
foreach($userChannel as $channel){
print_r($channel->getChannel());
}
I get two errors:
Notice: Undefined index: user in /Doctrine/ORM/Persisters/BasicEntityPersister.php on line 1396
Warning: Invalid argument supplied for foreach() in /Doctrine/ORM/Persisters/BasicEntityPersister.php on line 1401
**205**
But I still get the right channel id (the id in with the stars).
I have tried many things, but I still can't, by getting a User, get the Channel related to that User nor by getting the Channel, get the Users related to that channel. Am I using the relations correctly? Would really appreciate the help! Thanks!
Made same mistake myself when I was starting off with Doctrine.
You cannot mix Column and JoinColumn annotations. Try removing Column annotation from user and channel properties within UserChannel...
I've broken my head trying to set annotations for my 2 entities in order to use join tables.
I have 2 tables: user and cart.
Cart contains user ids with products which users selected.
1 user can have many products, so it's a ManyToOne relationship.
When I tried to set up the associations I tried a lot of different things and none of them worked. Now I'm having this:
// ---------------- USER CLASS ------------------- //
/**
* #ORM\Entity
* #ORM\Table(name="user", indexes={#ORM\Index(name="firstname_idx", columns={"firstname"}), #ORM\Index(name="lastname_idx", columns={"lastname"}), #ORM\Index(name="email_idx",columns={"email"})})
*/
class User
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* #ORM\Column(type="string", length=100)
*/
public $firstname;
/**
* #ORM\Column(type="string", length=100)
*/
public $lastname;
/**
* #ORM\Column(type="string", length=100, nullable=true)
*/
public $email;
/**
* #ORM\OneToMany(targetEntity="Cart", mappedBy="users")
**/
public $cart;
public function __construct() {
$this->cart = new ArrayCollection();
}
//...
And the Cart class:
/**
* #ORM\Entity
* #ORM\Table(name="cart")
*/
class Cart {
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* #ORM\Column(type="integer", nullable=true)
*/
public $user_id;
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="cart")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", onDelete="CASCADE")
**/
public $users;
public function __construct() {
$this->users = new ArrayCollection();
}
When I execute a query with join of these tables I receive the message:
"[Semantical Error] line 0, col 84 near 'u': Error: Class Cart has no association named User"
My query looks like this:
$q = $db->em->createQueryBuilder()
->select('c.*, u.*')
->from("Application\Entity\Cart","c")
->join("c.Application\Entity\User","u")
->getQuery();
What am I doing wrong? How to set the annotaions?
I'll appreciate any help.
Well, I figured it out by myself and again I'm answering my own question.
I spent several days on trying to fix the annotations. But the real problem was in the query.
I was trying to join entities as I was always doing it in Doctrine1. But in Doctrine2 when you join tables you have to join with the parameter you defined in the entity as the join parameter. So in my case it would be:
$q = $db->em->createQueryBuilder()
->select('c, u')
->from("Application\Entity\Cart","c")
->join("c.users","u")
->getQuery();
That was tricky for one that is used to work with doctrine1...
I have the following situation:
/**
* #Entity
* #Table(name="users")
*/
class Users{
/**
*
* #Id #Column(type="integer")
* #GeneratedValue
*/
protected $id_user;
/**
* #OneToMany(targetEntity="Items",mappedBy="user")
*/
private $items;
//this method sould return distinct items by name or group by name
public function getItems(){
return $this->items;
}
}
/**
* #Entity
* #Table(name="items")
*/
class Items{
/**
*
* #Id #Column(type="integer")
* #GeneratedValue
*/
protected $id_item;
/**
*
* #Id #Column(type="string")
*/
protected $name;
/**
* #ManyToOne(targetEntity="User",inversedBy="items")
* #JoinColumn(name="id_user", referencedColumnName="id_user" ,onDelete="CASCADE")
*/
protected $user;
}
In my view i send an $UserEntity, my problem is that i want to display only items that have unique name.
So If i do $items=$userEntity->getItems(); i receive all user items, not only unique one.
How can i solve this situation?
Thanks
select distinct with DQL should do the trick
I have two classes:
User:
/** #Entity #Table(name="users") */
class User {
/**
* #Id #GeneratedValue #Column(type="integer")
* #var integer
*/
protected $id;
/**
* #Column(type="string", length=20, unique=TRUE)
* #var string
*/
protected $login;
/**
* #OneToMany(targetEntity="News", mappedBy="author")
*/
protected $news;
public function __construct() {
$this->news = new \Doctrine\Common\Collections\ArrayCollection;
}
}
and News:
/** #Entity #Table(name="news") */
class News {
/**
* #Id #GeneratedValue #Column(type="integer")
* #var integer
*/
protected $id;
/**
* #Column(type="string", length=100)
* #var string
*/
protected $title;
/**
* #Column(type="text")
* #var string
*/
protected $content;
/**
* #ManyToOne(targetEntity="User", inversedBy="news")
* #JoinColumn(referencedColumnName="id")
*/
protected $author;
}
When I will make this code
$q = $this->db->createQuery('SELECT u FROM User u WHERE u.id = '.$id);
$user = $q->getSingleResult();
does the Doctrine will download all news (created by particular user) from database?
It won't until you request a user's news by calling the news accessor method. If you rewrite your query as
SELECT u, n FROM User u JOIN u.news n
Then a user and his news will be all returned as the result.
You can also check the query generated by doctrine using a logger:
$config->setSQLLogger(new Doctrine\DBAL\Logging\EchoSQLLogger);
PS: never do this: u.id = '.$id in queries to avoid sql injections. Instead, use placeholders:
$q = $this->db->createQuery('SELECT u FROM User u WHERE u.id = :id');
$q->setParameter('id', $id);