Symfony deleteaction multi table delete - symfony

Hi I want to delete the product in the other table when I delete a product. And I want to do this on the controller.
$deleteQuery = $this->getDoctrine()
->getManager()
->createQueryBuilder('d')
->delete('TechnoSiteBundle:Sepet', 's')
->where('s.id = ' . $request->get('id'))->getQuery();
$deleted = $deleteQuery->getResult();
OR
$deleteQuery = $em->getRepository('TechnoSiteBundle:Ebatlar')->createQueryBuilder('table1')
->where('table1.id')
->leftJoin('table1.sepet', 'table2')
->andWhere('table2.ebat1')
->delete('table1.id = table2.productid')
->getQuery();
$deleted = $deleteQuery->getResult();
I want to delete two related products. I get this error when I use Cascade;
2/2DBALException: An exception occurred while executing 'DELETE FROM ebatlar WHERE id = ?' with params [30]:

It's not very clear what you should do with so little information about your entities.
Some things to explore:
If your two product rows are related, you might want to look into using a cascade={"remove"} annotation somewhere.
Nothing prevents you from running two queries if you want to manually delete two entities.
Some notes about best practice with Doctrine, Symfony and MVC in general (not directly related to your question, and you don't have to follow these guidelines, but it's things you definitely should follow as that will make your life much easier in the long run):
You're not supposed to put too much logic in controllers. In Symfony, you want your controllers to be almost empty. You create a service with the logic in it, and call it from your controller in the appropriate action. This has many advantages that you'll notice as your project grows: less code duplication, the freedom to change your model without changing your controllers, etc.
It's better to bind parameters than to use concatenation like ->where('s.id = ' . $request->get('id')). This $request->get('id') is something the user can forge to insert basically anything he wants in your where clause.

Related

symfony3 doctrine2 association counter field

Consider the following case: I have two entities: Article and ArticleComment:
// \AppBundle\Entity\Article
/**
* #ORM\OneToMany(targetEntity="ArticleComment", mappedBy="article")
*/
private $comments;
I need to store the amount of comments in a field on the article (eg. articles.comments_count). The field needs to be updated whenever a comment is created or deleted.
Previously I used the CakePHP framework which has built-in CounterCache behavior which does this automatically. I've tried my best to find something similar for Doctrine 2 (starting with DoctrineExtensions library) but nothing seems to do what I'm looking for.
Any library that does this? Or do I have to come up with my own solution?
Edit: I've tried using Entity Events but I require this behavior on many entities so I'm interested in a reusable solution
You can take a look at the extra lazy associations. http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/extra-lazy-associations.html
This way you don't need to store the comment_counter as you will be able to use the count() function on your collection without loading the full collection.
Internally, Doctrine will issue a "select count" query.
Here is another answer which avoids storing this kind of aggregate and enables you to use the paginator as you've requested in comments. I didn't test it yet so there could be some errors.
$qb = $em->createQueryBuilder();
$qb
->select('a.title, a.author, count(c)')
->from('Article', 'a')
->leftJoin('a.comments', 'c')
->groupBy('a.id');
$paginator = $this->get('knp_paginator');
$pagination = $paginator->paginate($qb, $page, $limit);
As I said, this issue is not really Doctrine related because your initial model design is bad.
Usually, you don't need to store an aggregate which can be computed with a count/groupby query.
This kind of aggregate is useful when you have a lot of joined entities which creates a real overhead during computing. Else, you don't need it.

Doctrine2 Paginator, getArrayResult

I'm dealing with a fairly complex querybuilder (in a repository function), which is using a lot of things like partial c.{id,name,created} and a bunch of fetchjoins to try and keep both the amount of data and the amount of queries down.
This is however not enough, but the front end representation is paged anyway, so I'd like to make some ajax calls to fetch data when needed.
$qb = $this->createQueryBuilder('c')
->select('a whole bunch')
->join('many joins')
->setFirstResult(0)
->setMaxResults(10)
The above method doesn't work for complex joins, I do setMaxResults(10) and get 2 results, and setMaxResults(1000) gets me 118 results. Doctrine advises to use their Pagination class, as it will handle the count/iteration properly.
Now that works all fine if I loop over the Iterator object provided by new Paginator($query, true), but the code calling the repository function expects to get an Array from getArrayResult.
The iterator contains full entity objects, which means I would have to rewrite all the services to use methods instead of array keys.
Is there a way to use the Paginator with an ArrayResult?
Add Hydration Mode to your query.
//Set query hydration mode
$query->setHydrationMode(\Doctrine\ORM\Query::HYDRATE_ARRAY);

Symfony2 best way of removing business logic from controller and correct usage of model

I'm in searching of the best way of removing business logic from controller and correct usage of model(and maybe services).
Some details below.
Actually, my project is more complicated, but as example I will use Simple Blog application.
I have created my application (Simple Blog) in next steps:
created bundle
generated entities(Topic, Post, Comment)
generated controller for each entity, using doctrine:generate:crud
installed FOSUserBundle and generated User entity
So, I have all needed methods and forms in my controllers. But now I have some troubles:
Admin need to be able see all topics and posts, when simple User can only see
topic and posts where he is owner.
Currently there are indexAction, that return findAll common for any user. As solution, I can check in action, if ROLE_USER or ADMIN and return find result for each condition. But this variant keep some logic at action.
I also can generate action for each role, but what happened if roles amount will increase?
What is the best way to solve this problem with result for each role?
I need to edit some parameters before saving.
For example, I have some scheduler, where I create date in some steps, using features of DateTime.
Before saving I need to do some calculations with date.
I can do it in controller using service or simple $request->params edit.
What is the best way to edit some $request parameters before saving?
My questions I have marked with bold.
Thanks a lot for any help!
What I would do is to create a query which fetches the topics. Afterwards I would have a method argument which specifies if the query should select only the topics for a certain user or all topics. Something like this should do the work in your TopicRepository:
public function findTopics($userId = false)
{
$query = $this->createQueryBuilder('topic');
if($userId) {
$query->join('topic.user', 'user')
->where('user.id = :user_id')
->setParameter(':user_id', $userId)
;
}
return $query->getQuery()->getResult();
}
So, whenever you need to get the topics only by a user, you would pass a $userId to the method and it would return the results only for that user. In your controller you'd have something similar to this code (Symfony 2.6+):
$authorizationChecker = $this->get('security.authorization_checker');
if($authorizationChecker->isGranted('ROLE_ADMIN')){
$results = $this->get('doctrine.orm.entity_manager')->getRepository('TopicRepository')->findTopics();
} else {
$results = $this->get('doctrine.orm.entity_manager')->getRepository('TopicRepository')->findTopics($this->getUser()->getId());
}
You can try using Doctrine Events and create a PreUpdate depending on your case. See the documentation for more information. If you have a TopicFormType, you could also try the form events.
You are not supposed to "edit" a $request, which is why you can't directly do that. You can, however, retrieve a value, save it as a $variable and then do whatever you want with it. You can always create a new Request if you really need it. Could you be more specific what you want to do here and why is this necessary?

Symfony2: establish a model relationship by id

I'm building an API on Symfony2 an I have a Model ItemOrder with a ManyToOne relationship to a Model Item. I have a few Items in my database, and I want to add an ItemOrder that points to an Item that is already in the database, and whose id I know. So my first approach is this:
$item = new Item();
$item->setId(2);
$orderItem = new OrderItem();
$orderItem->setItem($item);
$em->persist($orderItem);
$em->flush();
However, Symfony2 understand that I'm trying to create a new item. I know that a valid approach would be fetching the Item object with the entity manager, and then assign it to the ItemOrder, but I think it's a not very efficient way of doing it.
So how should this be done?
What you're looking for is called Partial Reference.
$item = $em->getPartialReference('Item', 2);
$orderItem = new OrderItem();
$orderItem->setItem($item);
$em->persist($orderItem);
$em->flush();
However, please read the What is the problem? paragraph carefully, it might be safer to query for full entities by ids instead.
getPartialReference() vs getReference()
I've originally also found what forgottenbas linked, but I don't think it's the correct solution.
These two methods seem to be almost identical and both are referenced in official documentation.
Seems that only reasonable way to determine which is best is by looking directly into source code: getReference() and getPartialReference().
Straight up you will notice that getPartialReference() is better documented with a clear description of a use case that exactly matches yours:
* The use-cases for partial references involve maintaining bidirectional associations
* without loading one side of the association or to update an entity without loading it.
If you investigate the code for getReferece() you will notice that in some cases it will result in a database call:
if ($class->subClasses) {
return $this->find($entityName, $sortedId);
}
and finally, getPartialReference() marks partial reference as read-only, better defining it's purpose:
$this->unitOfWork->markReadOnly($entity);
You can create special reference object. More info see on this question
$item = $em->getReference('FQCNBundle:Item', 2);
$orderItem = new OrderItem();
$orderItem->setItem($item);
$em->persist($orderItem);
$em->flush();

Symfony 2: filtering x-to-many relations

I have two entities that represent users (User) and friendship requests (FriendshipRequest). There is a oneToMany relationship between User and FriendshipRequest, so Doctrine creates a method that is called getFriendshipRequests() in the class User. This is ok, but FriendshipRequest has an attribute that is called status, so I would like that the User class could filter the friendship requests associated to it attending to their status. I have read Doctrine documentation, and I found out this:
Natively you can’t filter associations in 2.0 and 2.1. You should use
DQL queries to query for the filtered set of entities.
According to this, I suppose that I should create a FriendshipRequest repository and create a method called "findByStatusAndUser" (or something like that), but I think that's a crappy solution.
I would like to have a method in the User entity, like getPendingStatusRequests(). Is this possible? If it isn't, what would be the best solution?
As of Doctrine 2.3 you can use matching and Criteria.
Then you could use getPendingStatusRequests() in User entity just like you wanted.
For your example the code would look like this:
public function getPendingStatusRequests()
{
$criteria = Criteria::create(); //don't forget to use Doctrine\Common\Collections\Criteria;
$criteria->where(Criteria::expr()->eq('status', 1));
return $this->friendshipRequests->matching($criteria);
}
I think that "getPendingRequestsForUser($user)" method in the FriendshipRequest repository should be a good solution. Inside this method you just need to create an appropriate DQL.
This is a good solution, because all of the logic should be moved to repositories, leaving entities as small and clean as possible.
UPD: Also, you could use findBy method, as described here, ex:
$pendingRequests = $em->getRepository('MyBundle:FriendshipRequest')->findBy(
array('user' => $user->getId(), 'status' => 1)
);
But for me, first method is preferred.
You can certainly add getPendingStatusRequests() to user and then have it cycle through all the friendship requests and only return those with the appropriate status.
The only potential problem is that all of the friendship requests will always be loaded including those you don't need. It is up to you to decide if this is a real problem or not. It might be that once a friendship request is processed then it is removed so a user won't have many requests at any given time.
If you do want to avoid loading all the requests then make a query and use the WITH expression on your join clause. Something like:
$qb->leftJoin('user.friendshipRequests','request',
Expr\Join::WITH, $qb->expr()->eq('request.status', $qb->expr()->literal('Pending')));
And since you are using S2 I would not fool around with repositories. Just make a service called UserManager, inject the entity manager, and give it a method called loadUserWithPendingFriendshipRequests.

Resources