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();
Related
I want to do horizontal partitioning for the "users" table which is having a large number of rows. So I split the table and will have then users_1, users_2 etc. These tables are generated dynamically.
My issue is how to set the table name dynamically in laravel models. I have tried below option and it works fine.
$hash = 1;
$user = new User();
$user->setTable('users_'. $hash);
$user->where('id', 23)->get();
Here I get the result from the users_1 table;
But when I call
User::all();
It is using the table users and not users_1.
I have also tried by using setTable() in the __construct method of model. But the issue is $hash is calculated based on the value used in controller which is not getting in the construct method of model.
Is there any solution for this?
You can make a scope that switches the "from" part of the query builder
add this in the model -
public function scopeFromTable($query, $tableName)
{
$query->from($tableName);
}
Then use
$DynamicTableData = ModelName::fromTable($tableName)->get();
you can use all Eloquent methods by this approach
It is due to User::all() is called statically getting a new Model class object and looking for table users by default. If you can use the instance you have created $this with setTable() then you can call with dynamic table names. $this refers the member variables and function for a particular instance.
Best solution for your case, would be to use the database level partitioning. So you don't need to manage the hashing. Just give a partitioning key static or range, with created_at field and then you can call to a single table with User::all() to get all the users and no need to call dynamically. Or can checkout database shard.
May be this can help you:
$data = new Model;
$data->setTable('users');
dump($data->get());
$data->setTable('users_status');
dump($data->get());
die;
Good Luck.
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.
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?
I have some personal data structure mixed with "standard fields". I would like to avoid the manual work on simple fields (with datastore native API):
toPersist.setProperty("field1", value1);
toPersist.setUnindexedProperty("field2", value2);
but I still want to get the prefilled Entity instance toPersist so I can add my own #Ignore fields my self
For example:
Entity filled = OfyService.ofy().save().entity(this).fill();
filled.setProperty("mySpecialField", jsonValue);
//...
// I want to save my entities alone
datastore.put( filled );
reversely I'd like to get the Entity object representing each entry in a load() call.
Is this possible? or do I have to dive into Objectify code to hack it?
thanks for your answers
I don't follow your question exactly, but I'm pretty sure what you're looking for are the #OnLoad and #OnSave annotations. You add them to methods within your entity classes, and those methods will be called just after an entity is loaded, or just before one is saved, respectively. The documentation for them is here.
Edit:
After your comments (below) I now understand what you are trying to accomplish. Yes, Objectify supports this (though I have never tried it myself). You want to use the Saver.toEntity() and Loader.fromEntity() methods. It appears you can use them like this:
// Use Objectify to convert a POJO into an Entity
Entity filled = ofy().save().toEntity(myPojo);
// Use Objectify to convert an Entity into a POJO
Object pojoCopy = ofy().load().fromEntity(filled);
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.