I have tables like
profile status
Profile.class
id name
1 taro
2 jiro
3 john
Status.class
id profile school date
1 1 highschool 2017-04-01
2 1 juniorhighschool 2013-04-01
3 2 highschool 2017-04-01
Status is added when status changes.
So I normally choose latest status every time I need status.
$ss = $this->em->createQuery(
"SELECT cm FROM UserBundle:Status s where c.profile = :p order by desc")
->setParameters(['p' => $profile])->getResult();
$ss[0] // Latest Status
So now I would like to put this in function.
What I want to do is getting latest status from profile.
I have a few ideas
Put this function in Profile Entity?
put this function in Profile Repository?
put this function in service???
In my opinion it should be the feature of Profile Entity, So I would like to put this in Entity though, access another from an Entity is bad manner.
Is it OK to access another entity from a Profile Repository??
Or should I use service??
You can achieve this with a method in ProfileRepository
<?php
public function getLastStatusByProfile(Profile $profile)
{
// do our query from Profile with a join on Status
}
And please, use a LIMIT 1 on your query, you only need the last result
You cannot put this in an Entity because Entities cannot be injected the Doctrine EntityManager dependency ($this->em).
To perform your "getLatestStatus()" function you need the EntityManager $this->em.
To access the EntityManager you can:
get it from the container in a Command or a Controller (e.g. in a Controller $this->get('doctrine')->getManager();)
inject it into a Service using Dependency Injection configuration files (see http://symfony.com/doc/current/service_container.html#injecting-services-config-into-a-service)
use it in a Repository because Repositories have native access to it
Usually people put functions such as getLatestStatus() in a repository, the repository becomes "the class where we put all DQL queries" and this works quite fine. This is recommanded by the official Documentation (https://symfony.com/doc/current/doctrine/repository.html) "Methods containing your query logic can then be stored in this class."
It is usual in Symfony Applications to have:
Entities having only properties, getters, setters, and some additionnal logical functions (like activate(), disable() ... functions that modify the Entity properties)
Repositories to hold DQL queries with complex logic such as getLatestStatus()
Services to hold other any other functions that read / modify data
Controllers are only gateways to use Services
So one complete example would be:
<?php
class ProfileRepository extends EntityRepository
{
/**
* #param Profile $profile
*
* #return Status
*/
public function getLatestStatus($profile)
{
$qb = $this->getEntityManager()->createQuery(
"SELECT cm FROM UserBundle:Status s where c.profile = :p order by desc")
->setParameters(['p' => $profile])
->getResult();
return $result;
}
}
And do not forget to handle the case where there is no "status" available for this profile. Do you wish to return null, raise an Exception or return a default status ?
Related
I have an object $user that has a one to many relation with $establishment. I can use:
$user->getEstablishments();
The user can select a stablishment to work on. I have this method that I call in the controller:
$user->setCurrentEstablishment($establishment);
And this one that I call in the view:
$establishment = $user->getCurrentEstablishment();
I want to be able to call:
$user->setCurrentEstablishmentBy Slug($establishment_slug);
where the slug is a string, and let the user object look for the establishment.
Doctrine discourages the practice of accessing the Entity Manager inside the Entity object, but I think that using it in the controller is even worse.
I suspect that some special Doctrine annotation exists that takes care of non persistent relations like this, or some method other than serving the Entity Manager through a service should be used here. Some easy way of referencing other entities from inside the model.
¿Is there any? ¿How could I do that?
There is no Annotation in Doctrine which could convert slug into object.
What can help You is ParamConverter, with it you can automatically convert slug from query into object. But it still must be used in Controller.
Example usage:
/**
* #Route("/some-route/{slug}")
* #ParamConverter("object", class="AppBundle:Establishment", options={"id" = "slug", "repository_method" = "findEstablishmentBySlug"})
*/
public function slugAction(Establishment $object)
{
...
Docs about param converter: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html
I would like to retrieve a record from another entity (or record from the DB) within a entity.
They there are no relationship between the two entities.
I am using #ORM\HasLifecycleCallbacks() and #ORM\PrePersist so when the main entity is created it will also create another entity (save a record to another table)
The above is working fine, there are no issues with this.
What I am having an issue with is I would like to link that entity with another table but I need to retrieve the object based on the value of the first entity.
Usually I would write a function in the entity repository but I am not calling the entity manager within the entity.
An Entity in Doctrine is an object representation of a concept, with attributes and methods. It is meant to be lightweight, a POPO (plain old php object). It must not know anything about its persistence. Therefore if you see reference to the EntityManager in a model, it probably stinks.
Solutions? You could use an entity listener called on entity creation and then use a service dedicated only to properly compose your object(s), maybe something like a Factory. In this way, your entity stays lightweight, the lifecycle management is satisfied and the entity composing is responsibility only of your service.
Entity manager is accessible in an entity repository. You can legally use it to fetch data from other entities and to compose your business logic. This is what entity repositories are made for: Doctrine Custom Repositories, Symfony Custom Repository Classes.
/**
* #ORM\Entity
*/
class Beta {}
/**
* #ORM\Entity
*/
class Alpha {}
class AlphaRepository extends EntityRepository
{
public function getDataFromAnotherEntity($something)
{
$query = 'select * from MyBundle\Entity\Alpha alpha where alpha.id = :something';
return $this->getEntityManager()
->createQuery($query)
->setParameter('something', $something)
->getResult();
}
}
In Symfony 3.1 you can use the entityManager to set a reference. This is still lightweight as it does not instance a complete Doctrine Record.
Example: I have an entity Status which has some states, and it's referenced in another entity. On create i use this method inside EventSubscriber:
public function preAction(LifecycleEventArgs $args)
{
$entity = $args->getObject();
$entityManager = $args->getObjectManager();
if (method_exists($entity, 'setStatus')) {
if ($entity->getStatus() === null) {
$entity->setStatus($entityManager->getReference('AppBundle\Entity\Status', Status::STATUS_REGULAR));
}
}
}
I'm trying to access private properties of an entity from his own repository. By doing PHP tells me I can not access private or protected properties or methods.
I'm just calling a custom repository method created by me, passing an instance of the entity. When i try to get the ID for example, php throws me the error.
How I can access it?
public function customMethod($entityInstance)
{
$query = $this->getEntityManager()
->createQuery(
'SELECT c
FROM AcmeMainBundle:Content c
WHERE
c.published = 1
AND
c.id != :id
ORDER BY c.date DESC'
)
->setParameter('id',$entityInstance->id);
return $query->getResult();
}
This is an example of the custom method of my repository. Obviously is just an example, that DQL is not very usefull but when I try to access to $entityInstance->id ...
Can someone helps me?
The relationship between a Repository and an Entity is conceptual, not structural. This means that although you know they are related, PHP doesn't.
If you want to access private members of the Entity from the Repository you will have to do it like with any other class: using getters and setters.
Try this:
->setParameter('id', $entityInstance->getId());
Well, as with all normal PHP scripts, if you want to access a private or protected property you have to create a getter. DQL isn't changing anything from the PHP site, it only adds minor changes to the SQL syntax (which is just a string in PHP).
So actually, I don't see what you're trying to say with this question. That DQL should change the way PHP and OO works?
I have an issue when erasing something from a BD.
The problem is that it not only erase the object i looked for (using findOneBy), but all the objects related to the principal id.
//---Controller
$new = $this->getDoctrine()->getManager();
$OBJcar = $new->getRepository('SomeOtherBundle:CarEntityClass')
->findOneBy(array('idOwner' => $idowner, 'idCar' => $idcar));
if($OBJcar){
$new->remove($OBJcar);
$new->flush();
$msj="The car for an specific owner has been erased.";
}
//---Profiler (Query)
"START TRANSACTION"
Parameters: { }
Time: 0.22 ms
DELETE FROM schema.CarTable WHERE id_owner = ?
Parameters: ['123456']
Time: 0.63 ms
"COMMIT"
Parameters: { }
Time: 0.63 ms
How to erase the one row i am getting from the db?
I voted down the answer above, because I'm tired of people using string DQLs.
It's not standartized, non-object oriented(even though in background dql operates with objects), it doesn't use caching mechanisms query builder provides, it's non-flexible and simply looks unclean.
Here is the "right way"(IMHO):
You add the repository class for entity
You add the method you need with a query builder in it
You call the method while passing parameters needed for specific REPOSITORY OBJECT ORIENTED ACTION
You get an easy-to-handle result
Here's the code:
namespace ProjectName\BundleName\Repository;
use Doctrine\ORM\EntityRepository;
class CarRepository extends EntityRepository
{
public function deleteCarWithOwner($ownerId,$carId)
{
$isDeleted = $this->createQueryBuilder("car")
->delete()
->where('car.id = :carId')->setParameter("carId", $carId)
->andWhere('car.idOwner = :ownerId')->setParameter("ownerId", $ownerId)
->getQuery()->execute();
return $isDeleted;
}
}
Also, refer to http://doctrine-orm.readthedocs.org/en/latest/reference/query-builder.html for query builder details. There are a lot of "pros" and I see no "cons" for using builder.
LATE UPDATE
Also, a lot of Doctrine's entity events are not dispatched when using DQL.
Use DQL
$query = $em->createQuery('DELETE SomeOtherBundle:CarEntityClass c WHERE c.idOwner = 4 AND c.id = 10');
$query->execute();
This will remove only single car with ID 10 and owner with ID 4.
I'm building an application where users are tied to accounts (as in, multiple users all share an account), then other entities - lets call them products are tied to the accounts. The products are associated with the accounts and only users that are tied to that account can edit the products.
The difference being in my case, there are many different entities being shared in the same model.
If it was just the one (product) entity, it wouldn't be a problem to have a method in my ProductRepository like:
public function checkOwnership($id, $account)
{
$count = $this->createQueryBuilder('s')
->select('count(s.id)')
->where('s.account = :acc')
->andWhere('s.id = :id')
->setParameters(array('id' => $id, 'acc' => $account))
->getQuery()
->getSingleScalarResult();
if($count == 0)
throw $this->createNotFoundException('ye..');
return;
}
To make sure the id of the product is tied to the user's account.
The account argument is passed to this function by using:
$this->getUser();
In the controller.
What is the best way for me to make this function available to the other entities to prevent code duplication? There are (currently) only 3 other entities, so it wouldn't be the end of the world to just have it in each repository, but I'm just wondering if there were a way to create a 'common' or global repository that follows good standards? I'd sure like to know about it.
Edit:
Or have I just completely over-thought this? Can I just create a 'Common' directory in my 'MainBundle', define a namespace and add a use statement at the start of my controllers that need access to the function?
I hope I fully understand your question.
Solution one, duplication, easy: let #ParamConverter do the job (automatic response to 404 if doesn't exist)
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
/**
* #Route("/pets/{id}")
*/
public function showAction(Pet $pet)
{
// Current logged user
$currentUser = $this->get('security.context')->getToken()->getUser();
// Owner
$ownerUser = $pet->getUser();
if(!$currentUser->equals($ownerUser)) {
// 401, 403 o 404 depends on you
}
}
Solution two, DRY, a bit harder: use JMSAOPBundle and define an interceptor that intercepts all request to you controller and actions (show, delete, etc.). The pointcut (connected to the interceptor) will get the current user from the context and the owner from the entity. Then you do the check...