How insert very reference in doctrine - mysql

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);
}

Related

Symfony4.1 Doctrine ManyToMany Reduce No of Queries

I'm working on a project. Entity are Blog,Category,Tags. Blog and Tags are in ManyToMany Relation. My repository query to fetch data by Tags filter is.
CODE1:
/**
* #return BlogPost[]
*/
public function getAllActivePostsByTags($value, $order = "DESC", $currentPage = 1, $limit = 10)
{
$query = $this->createQueryBuilder('p')
// ->select('p','t')
->innerJoin('p.blogTags', 't')
->where('t.slug = :val')
->setParameter('val', $value)
->orderBy('p.id', $order)
->getQuery();
$paginator = $this->paginate($query, $currentPage, $limit);
return $paginator;
}
This code works fine. All the tags(No of tags in a post)are displayed correctly. But the No of DB Query is 14. Then When I uncomment select as this,
CODE2:
/**
* #return BlogPost[]
*/
public function getAllActivePostsByTags($value, $order = "DESC", $currentPage = 1, $limit = 10)
{
$query = $this->createQueryBuilder('p')
->select('p','t')
->innerJoin('p.blogTags', 't')
->where('t.slug = :val')
->setParameter('val', $value)
->orderBy('p.id', $order)
->getQuery();
$paginator = $this->paginate($query, $currentPage, $limit);
return $paginator;
}
No of Query is 9. But The Tags per Post is only one(Not displaying all the tags of a single post).
To be clear info:
It displays entire list of BlogPost.
But not all Tags of a Post.
Only one Tag per Post is shown.
Question: Is code1 is correct (No of DB Query = 14) or Do I have to tweak little bit to reduce no of DB Hits. Please guide me on this.
This is the expected behaviour in both cases.
Case 1) You just select the BlogPost entities. So you tell doctrine to fetch all BlogPosts that have the BlogTag that has slug = value.
The SQL query produced returns only column values from the blog_post table and so only hydrates the BlogPost entities returned, it does not hydrate the collection of BlogTags inside each BlogPost.
When you try to access the tags of a BlogPost a new query is generated to get and hydrate its collection.
That is the reason you get more queries in this case.
Case 2) You select also the filtered BlogTag entities, and doctrine hydrates(puts) only this filtered BlogTag to each BlogPost `s collection.
When you try to access the BlogTags of a BlogPost, you get the filtered one that meets the condition in the querybuilder.
To force doctrine to "reload" the data from the database, you should refresh the blogPost entity:
$em->refresh($blogPost);
and also include refrech option on cascade operations of the relation definition:
#OneToMany(targetEntity="BlogTag", mappedBy="post", cascade={"refresh"})
References:
what cascade refresh means in doctrine 2
refresh objects: different question but same solution
Thanks #Jannes Botis for refresh. But in my case the code itself is wrong. There need a slight change in it.
BlogTags.php
/**
* #ORM\ManyToMany(targetEntity="BlogPost", mappedBy="blogTags")
*/
private $blogPosts;
BlogPost.php
/**
* #var Collection|BlogTags[]
*
* #ORM\ManyToMany(targetEntity="BlogTags", inversedBy="blogPosts", cascade={"refresh"})
* #ORM\JoinTable(
* name="fz__blog_n_tag",
* joinColumns={
* #ORM\JoinColumn(name="blog_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="tag_id", referencedColumnName="id")
* }
* )
* #ORM\OrderBy({"name": "ASC"})
*/
private $blogTags;
This created the join_table. Allready I have a join_table. Although This code is for reference to someone.
Controller.php
// This is my old Code
$bp = $em->getRepository('App:BlogPost')->getAllActivePostsByTags($slug, "DESC", $page, self::PAGE_LIMIT);
// This is my New Code
$bp = $em->getRepository('App:BlogTags')->getAllActivePostsByTags($slug, "DESC", $page, self::PAGE_LIMIT);
Repository.php
public function getAllActivePostsByTags($value, $order = "DESC", $currentPage = 1, $limit = 10)
{
$query = $this->createQueryBuilder('t')
->select('t','p','tx')
->innerJoin('t.blogPosts', 'p')
->innerJoin('p.blogTags', 'tx')
->where('p.isActive = :val1')
->andWhere('t.slug = :val2')
->setParameter('val1', true)
->setParameter('val2', $value)
->orderBy('p.id', $order)
->getQuery();
$paginator = $this->paginate($query, $currentPage, $limit);
return $paginator;
}
I not changed my old twig file completely. As it throws error at many places. Because now i'm using tags repo instead of blog. So i modified the twig with
{% include 'frontend/page/blog_home.html.twig' with { 'bp':bp|first.blogPosts } %}
Help me on this (twig file): There is only one tag, that's why |first twig filter
Clarify me with this twig filter. Do I'm doing right. Give me suggestion to improve on it. I tried bp[0] This trows error.
Finally: By using old code in controller it returns 14 db hits. Now it returns only 8. Even there are more tags in a post (but old one returns more).

Update MySQL database Symfony2

I have a MySQL database and frontend DataTables view and work with Symfony.
I have 2 buttons on my tables. Delete and edit.
If I click on delete, it deletes the row from the DataTables and from MySqL. Works with this code:
/**
* #Route("/delete/{id}", name="deletepage")
* #Template()
*/
public function delete($id)
{
$em = $this->getDoctrine()->getManager();
$events = $em -> getRepository('AppBundle:eqAli')->find($id);
$em -> remove($events);
$em -> flush();
return $this->redirectToroute('homepage');
}
I have an edit button, which redirects to an edit page, where the data from that row is auto-filled.
What I am trying to achieve is that once there is made a change by a user on that page and the user clicks on save, that the new information will be updated into MySQL. So no delete, but update.
I am trying to achieve that with this code:
/**
* #Route("/edit/update/{id}", name="updatepage")
* #Template()
*/
public function update($id)
{
$em = $this->getDoctrine()->getManager();
$events = $em -> getRepository('AppBundle:eqAli')->find($id);
$em -> persist($events);
$em -> flush();
return $this->redirectToroute('homepage');
}
But that does nothing. It does get the right ID, I can click, just no update into MySQL. The path etc is right because when I change persist into delete, the button works.
Anyone a solution?
Basically, you are fetching single eqAli entity, ordering Entity Manager to manage it and flush all changes to your entities into database.
The problem is that you are not making actual changes to your entity before saving.
Ehhh ...
All should be in editAction (no redirect to update route necessary) , below you have working example, try to implement it on your case (it's a little bit more advanced , but you should catch idea ) .
```
/**
* #ParamConverter(name="reklama", class="SomeBundle:Reklamy")
* #Template()
* #param Request $request
* #param Reklamy $reklama
*/
public function editAction(Request $request,Reklamy $reklama){
$em = $this->getDoctrine()->getManager();
$form=$this->createForm(new ReklamyType(),$reklama);
if($request->getMethod()=="POST"){
$form->handleRequest($request);
if($form->isValid()){
$em->flush();
return $this->redirectToRoute('someroute')
}
}
return array('form'=>$form->createView());
}
You need to do something with the $events entity that you found, like so:
$events = $em -> getRepository('AppBundle:eqAli')->find($id);
...
$events->modifyDate( new DateTime() );
...
$em -> persist($events);
$em -> flush();
Where for example modifyDate is a method that is part of the eqAli Entity. I show ... before and after to indicate you might need to do other changes to your entity. In your original code, you are not changing anything at all. That's why you don't see any differences in the database.

Find object through chained ManyToOne relationships

I'm trying to figure out how to find a group of objects based on a layered relationship. I have 3 entities like so:
Referral --> manyToOne --> Patient --> manyToOne --> Payor
How do I find all referrals a given payor?
I'm using symfony3 with mysql and doctrine. My entities:
class Referral
{
// usual stuff
/**
* #ORM\ManyToOne(targetEntity="Patient")
*/
private $patient;
}
class Patient
{
// usual stuff
/**
* #ORM\ManyToOne(targetEntity="Payor")
*/
private $payor;
}
class Payor
{
// usual stuff
}
Obviously I could make the relationships birectional, for example so I could do something like this in my controller:
$patients = $payor->getPatients();
foreach ($patients as $patient) {
$referrals = $patient->getReferrals();
}
And then collect these into an appropriate array, but this seems messy and I'd rather do it all in a single database query in my repository. Can that be done?
you can find all referrals for a given payor using a query.
in ReferralRepository
public function findReferralsByPayor(Payor $payor)
{
$qb = $this->createQueryBuilder('r');
$qb
->join('BUNDLENAME:Patient', 'p', 'WITH', 'p.id = r.patient')
->where('p.payor = :payor')->setParameter('payor', $payor)
;
return $qb->getQuery()->getResult();
}

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.

Doctrine2 multiple join

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.