Doctrine2 multiple join - mysql

I have problem with my User Entity. I have code generated by Doctrine it is below:
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="Frontend\UserBundle\Entity\SfGuardPermission", inversedBy="user")
* #ORM\JoinTable(name="sf_guard_user_permission",
* joinColumns={
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="permission_id", referencedColumnName="id")
* }
* )
*/
protected $permission;
Problem is with join because I can't join user and permission. What I must to do? I must join sf_guard_user with sf_guard_user_group with sf_guard_grop with sf_guard_group_permission with sf_guard_permission. Because I need to get User permission. I do not no how to write join like this in code above. Is it possible?

You can not write this join in one annotation. In fact you gone have three entity tables sf_guard_user, sf_guard_group and sf_guard_permission and two cross tables which you can write as you already started, sf_guard_user_group and sf_guard_group_permission.
But since it looks like you try to migrate some symfony 1.x stuff to symfony 2.x:
The sf_guard_user_permisson table in symfony 1.x is a cross table between users and permission, containing extraordinaire permission for a user which are not granted through the groups the user is in, so you are already done.

SBH thx for replay, of course you have right with everything what you have written. But my sf_guard_user_permisson is empty so I can't use it. I can generate this table, this is no problem, but then I will must maintain it. This is next work for me so i wrote code below:
namespace Frontend\UserBundle\Entity;
// ...
/**
* #var \Doctrine\Common\Collections\Collection
*
*/
protected $permissions;
/**
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getPermissions()
{
$groups = $this->getSfGuardGroups();
foreach ($groups as $group)
{
$groupPermisions = $group->getPermission();
foreach ($groupPermisions as $groupPermision)
{
if (!in_array($groupPermision, $this->permissions)) {
$this->permissions[] = $groupPermision;
}
}
}
return $this->permissions;
}
/**
* #param string $permissionName
* #return boolean
*/
public function hasPermission($permissionName)
{
$this->getPermissions();
foreach ($this->permissions as $permission)
{
if($permission->getName() === $permissionName) {
return true;
}
}
return false;
}
// ..
What do you think about it? Your opinion is very important for me.
Edit:
Thx for SBH help, I have got answer for my question. I have hope it will help other people. If you do not understand something please look at SBH answer.

Related

How to do a join on two entities in Symfony and Doctrine?

I have a select query and I'm trying to add a join to it.
In the example below, I have a Questionentity that I use to return some results, and I want to add a join with the User entity, like:
SELECT question FROM question AS q LEFT JOIN USER u ON q.user_id= u.id;
I would like the result to be a User entity inside a Question entity, something like:
private Question (entity)
private id
private user_id
private User (entity)
private id
private name
here is my class
namespace AppBundle\Repository;
use AppBundle\Entity\User;
use AppBundle\Entity\Question;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Tools\Pagination\Paginator;
class QuestionRepository extends EntityRepository
{
/**
* #param int $currentPage
*
* #return Paginator
*/
public function getQuestions($currentPage = 1)
{
$questions = $this->createQueryBuilder('question')
->where('question.active is NULL')
->getQuery();
$paginator = $this->paginate($questions, $currentPage);
return $paginator;
}
}
I call it like this
$questionRepo = $this->container->get('doctrine')->getManager()->getRepository('AppBundle:Question');
$questions = $questionRepo->getQuestions(1);
Any ideas?
How about that:
$questions = $this->createQueryBuilder('question')
->leftJoin('question.user', 'question_user', 'WITH', 'question_user.user = :user_id')
->where('question.active is NULL')
->setParameter('user_id', $user_id)
->getQuery();
$paginator = $this->paginate($questions, $currentPage);
Edit: Because of your latest comments, i have to mention, that this suggestion is assuming that your Question entity looks like this (according User's entity:
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\User", inversedBy="question")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
If not, add this, generate entities (php app/console doctrine:generate:entities AppBundle:Question) and update DB (php app/console doctrine:schema:update --force).
PS: Before generating entities, you'll have to remove old getters/setters.

How insert very reference in doctrine

I have 3 entities: User, Page, Post
In the Post entity:
class Post
{
/**
* #ORM\ManyToOne(targetEntity="Page")
*/
private $page;
/**
* #var ArrayCollection $subscribers Specify users that see this post.
*
* #ORM\ManyToMany(targetEntity="User", inversedBy="wallPosts")
*/
private $subscribers;
}
In the Page enity:
class Page
{
/**
* #ORM\ManyToMany(targetEntity="User")
*/
private $members;
}
When a post is published I want that all users that are members of Page are set as subsribers of Post
$post->setSubscribers($post->getPage()->getMembers())
note: Page can have several thousand users, what is best way that have best performance? Should I use a native query, database triggers or procedures?
You can do it in cycle. Subscribe 50 users per one iteration to new post.
Do not forget to clean doctrine internal storage. The main idea is
$page = $post->getPage();
for ($i = 1;; ++$i) {
$members = $page->getPagindatedMembers($i);
if (empty($members)) {
break;
}
foreach ($members as $member) {
$post->addSubscriber($member);
}
$em->flush();
$em->clear(Member::class);
}

Symfony Doctrine - Joins without foreign keys

I'm trying to set up a schema to capture twitter users and their followers.
I have two tables. TwitterUser and Follower. Follower has 3 fields - id, twitterUser, follower.
When a twitter user is a added to the table, I also add a row to Followers to join them with other users I may be interested in.
However, If I get Symfony/Doctrine to build the tables using something like the following-
/**
* #ORM\Entity
* #ORM\Table(name="follower")
* #ORM\HasLifecycleCallbacks
*/
class Follower
{
public function __construct()
{
}
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="TwitterUser", inversedBy="followers")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $twitterUser;
/**
* #ORM\ManyToOne(targetEntity="TwitterUser", inversedBy="following")
* #ORM\JoinColumn(name="follower_id", referencedColumnName="id")
*/
protected $follower;
...
It insists on creating a Foreign Key for follower that I don't want, as I don't want to have to get ALL twitter users to ensure that my joins always work.
The only way I can think to do it, is to remove the annotation and create the SQL for the join myself. Is there a smarter way to do it?
I suggest using a ManyToMany self-referencing relationship as describe below:
http://docs.doctrine-project.org/en/2.0.x/reference/association-mapping.html#many-to-many-self-referencing

Symfony2 tree builder - what does the method canBeUnset() do?

$rootNode
->children()
->arrayNode('form')
->info('form configuration')
->canBeUnset()
->treatNullLike(array('enabled' => true))
->treatTrueLike(array('enabled' => true))
->children()
->booleanNode('enabled')->defaultTrue()->end()
->end()
->end()
Line 5 of the above snippet from Symfony\Bundle\FrameworkBundle\DependencyInjection\Configuration uses the method canBeUnset(). I don't know what this does because it seems to not do anything if I remove it. I'm working understanding semantic configuration for my own bundles.
Following the code, you can find definition for this method in Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition class.
/**
* Sets whether the node can be unset.
*
* #param Boolean $allow
*
* #return ArrayNodeDefinition
*/
public function canBeUnset($allow = true)
{
$this->merge()->allowUnset($allow);
return $this;
}
This is passed to MergeBuilder ( Symfony/Component/Config/Definition/Builder/MergeBuilder ) which handles config merging.
/**
* Sets whether the node can be unset.
*
* #param Boolean $allow
*
* #return MergeBuilder
*/
public function allowUnset($allow = true)
{
$this->allowFalse = $allow;
return $this;
}
So my understanding is, that this method defines, if your config value can be unset while merging configurations, in case the overriding config does not support the value. I would have to test though, to find out the behaviour if the unsetting is not allowed, but I guess then it would throw an exception about a missing config value just like isRequired.

Order by within group by in Doctrine 2

I'm using Symfony 2 PR12 with Doctrine 2 and MySQL. I have a database storing articles and views of those articles:
// ...
class Article {
/**
* #orm:Column(type="bigint")
* #orm:Id
* #orm:GeneratedValue
* #var int
*/
protected $id;
/**
* #orm:OneToMany(targetEntity="ArticleView",mappedBy="article")
* #var ArrayCollection
*/
protected $views;
// ...
}
// ...
class ArticleView {
/**
* #orm:Column(type="bigint")
* #orm:Id
* #orm:GeneratedValue
* #var int
*/
protected $id;
/**
* #orm:Column(type="bigint",name="DateRead",nullable=true)
* #var int
*/
protected $viewDate;
/**
* #orm:ManyToOne(targetEntity="Article",inversedBy="views")
* #var Article
*/
protected $article;
// ...
}
I want to get, for example, the 20 most-recently-viewed articles. My first thought would be something like:
$qb = <instance of Doctrine\ORM\QueryBuilder>;
$qb->select('a')
->from('Article', 'a')
->join('a.views', 'v')
->orderBy('v.viewDate', 'DESC')
->groupBy('a.id')
->setMaxResults(20)
;
However, when there's more than one view associated with an article, the order-by/group-by combination gives unpredictable results for the ordering.
This is expected behavior for MySQL, since grouping is handled before ordering, and there are working raw-query solutions to this problem at http://www.artfulsoftware.com/infotree/mysqlquerytree.php (Aggregates -> Within-group aggregates). But I can't figure out how to translate any of these solutions into DQL, since as far as I can tell there's no way to select from subqueries or perform self-exclusion joins.
Any ideas on how to solve the problem with reasonable performance?
I ended up solving it with a correlated subquery:
$qb
->select('a')
->from('Article', 'a')
->join('a.views', 'v')
->orderBy('v.viewDate', 'DESC')
->setMaxResults(20)
// Only select the most recent article view for each individual article
->where('v.viewDate = (SELECT MAX(v2.viewDate) FROM ArticleView v2 WHERE v2.article = a)')
That way the sort ignores ArticleView's other than the most recent for any given article. Though my guess is that this performs fairly poorly relative to the other raw SQL solutions - any answers with better performance would still be greatly appreciated :).