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.
Related
In Symfony 5, let's say we have 3 entities linked like this :
Foo is an entity that have Bar as child. Foo as one property called fooProperty.
Bar have Foo as parent, and Baz as child
Baz have Bar as parent, of course. Baz have one property called bazProperty.
Let's say that the value of bazProperty is dependent of the value of the value of fooProperty. My first idea was to refer to foo entity inside the baz entity class :
function setBazProperty($value) {
if ($this->getBar()->getFoo()->getFooProperty > 0) {
$this->bazProperty = $value;
} else {
$this->bazProperty = 0;
}
}
But this occur many sql queries, as Doctrine will ask first to get Bar entity, then Foo entity.
So I imagine to access to Foo entity through an unique query managed in a repository class.
But, because of the separation of concern, I wouldn't inject the repository in the Baz entity, but I would use a service instead.
So I've created a BazService with two arguments in the constructor :
public function __construct(Baz $baz, BazRepository $bazRepository)
{
$this->baz = $baz;
$this->bazRepository= $bazRepository;
}
In this service, I've also added a method fetching Foo entity :
public function getFoo()
{
return $this->bazRepository->getFoo($this->baz);
}
And last, in a controller, now I would like to get Foo entity :
$bazService = new BazService($baz);
$foo = $bazService->getFoo();
Here are my questions :
I'm not able to initialize bazService in the controller. The constructor ask for 2 argmuments (the entity and the repository) and I would like only to provide the entity and to inject automatically the repository class.
I've tried to add it in serices.yaml without success (probably because I didn't instantiate bazService in the constructor of my controller) :
App\Service\BazService:
arguments:
$bazRepository: App\Repository\BazRepository
Is there any other solution ? How I can inject the entity class differently in the service class ?
Using a service when setting a property is too complex is the recommended solution ? Some article (here, here and here) recommend to use a service when the method inside entity class become more complex and require external entity or repositories. But maybe there's a lighter solution...
Separation of concerns is IMHO the right argument to look at. There are some approaches to go for, that depend largely on how you retrieve the entity. However, in my opinion, the concern of an entity is NOT to fetch some other entities data from the database, it is the repository's or maybe the controller's concern. So let's see how to do that ...
One way is to automatically retrieve the parent entity / entities. Depending on your use case, you could do that in general (via fetch="EAGER" - see: #ManyToOne / #OneToOne), otherwise you could implement a special repository function, that fetches the additional entities. If your entities always only have at most one parent each, this can absolutely reduce the number of queries from 3 to 1, since the parent and parent of parent entities can be retrieved simultaneously.
// in BazRepository
public function getWithParents($id) {
$qb = $this->createQueryBuilder('baz');
$qb->leftJoin('baz.bar', 'bar')
->addSelect('bar')
->leftJoin('bar.foo', 'foo')
->addSelect('foo')
->where('baz.id = :id')
->setParameter('id', $id);
return $qb->getQuery()->getOneOrNullResult();
}
if the child entity accesses the parent entity, it should just use the entity from cache and avoid a second query (source: https://symfonycasts.com/screencast/doctrine-relations/join-n-plus-one)
If having the entities is already "too much", you can slightly cheat by (again) creating a custom repository method that not only fetches the Baz entity, but also the Foo.fooProperty value and sets it for a virtual/temporary property on the Baz entity.
// in BazRepository
public function getWithFooProperty(int $id) {
$qb = $this->createQueryBuilder('baz');
$qb->leftJoin('baz.bar', 'bar')
->lefTJoin('bar.foo', 'foo')
->select('foo.fooProperty as fooProperty')
->where('baz.id = :id')
->setParameter('id', $id);
$result = $qb->getQuery()->getResult(); // should be an array with an array with two keys, but I might be wrong
if(count($result) == 0) {
return null;
}
$baz = $row[0][0];
$baz->fooProperty = $row[0][1];
return $baz;
}
(Disclaimer: please check the $result here, to see if the accesses are correct)
you now could access it in Baz:
function getFooProperty() {
if(isset($this->fooProperty)) {
return $this->fooProperty;
} else {
// fallback, in case entity was fetched by another repository method
return $this->getBar()->getFoo()->getFooProperty();
}
}
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 ?
I'm currently working on an API using the following stack;
Symfony (3)
FOSRestBundle
Fractal
I'm wanting to integrate the ability to specify, via query parameter, which relationships to include when retrieving an entity/collection, e.g;
[GET] /users?include=friends.addresses
Fractal comes with the ability to handle includes however, as this happens around the serialization point of the response building, each related entity is retrieved via lazy loading, thus triggering additional queries.
Is there a way to tell Doctrine, when retrieving a collection, to dynamically also retrieve relationships specified? Ive seen the following from the Doctrine docs which shows how to dynamically change the fetch mode however this only seems to work with associations on the target entity (friends in the example above) and not deeper relations (addresses of friends in the example).
Thanks!
If I remember correctly you can "preload" relations by joining them in rather than letting the lazy loading mechanism handle it. An idea could be to create a service that creates a query builder based on your criteria. This is a crude snippet of what I mean:
class EagerService
{
protected $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
public function resolveIncludes($class, $alias, $includes)
{
// Parse includes into an array
if (strpos($includes, '.') !== false) {
$relations = explode('.', $includes);
} else {
$relations = [$includes];
}
// The next relation is owned by the previous one, so we keep track of the previous relation
$previousRelation = $alias;
$qb = $em->getRepository($class)->getQueryBuilder($previousRelation);
foreach ($relations as $relation) {
// Add inner joins to the query builder referencing the new relation
$qb->innerJoin("{$previousRelation}.{$relation}", $relation);
$previousRelation = $relation;
}
// Return query builder or the result of the query
return $qb;
}
}
I'm developing a web app, and I might have logic such as "When Field A = value and Field B = other value, then this should be shown in the frontend." I might have different lists, which further elaborate on this. I might even have translated entities which have the translation in a different entity (example: Entity and EntityTranslation) and want to join them only on a specific locale.
How could I reuse all this logic, to avoid repeating the same QueryBuilder::andWhere() and QueryBuilder::join() (even QueryBuilder::select()) calls all over the place?
I've found http://www.whitewashing.de/2013/03/04/doctrine_repositories.html which talks on this, but I'm curious about solutions which involve also JOINs and SELECT.
EDIT:
Bad example of what I'd want:
$queryBuilder
->andWhere(FRONTEND_LOGIC)
->joinWithTranslationTable();
So I'd want to be able to compose "complex" queries from my, simpler, but Buisiness driven, parts.
You can use prepared statements in Doctrine's DBAL layer.
$dbal = $this->getDoctrine()->getConnection('default');
$stmt = 'SELECT name, birth from user where id = :id';
$user1 = $dbal->executeQuery( $stmt , array( 'id' => 1 ) )->fetchAll() ;
$user2 = $dbal->executeQuery( $stmt , array( 'id' => 2 ) )->fetchAll() ;
Refer to the documentation for other methods suitable for your task:
http://docs.doctrine-project.org/projects/doctrine-dbal/en/2.0.x/reference/data-retrieval-and-manipulation.html#using-prepared-statements
Doctrine does not allow you to specify the query builder class so you cannot directly extend it. You can however decorate it.
class MyQueryBuilder
{
public function __construct($doctrineQueryBuilder) { this->doctrineQueryBuilder = $doctrineQueryBuilder; }
// Your custom functions
public function addWhereFrontEndLogic ...
public function joinWithTranslationTable ...
// Also need to add the the regular methods that you use
public function addSelect($value) { return $this->doctrineQueryBuilder($value); }
// You would then
$qb = new MyQueryBuilder($this->createQueryBuilder());
Bit of a pain to have to add all the standard functions but once you have done it then you can add as much custom business logic as you want.
There are some other possible approaches. You could add the custom methods to a base repository class and then just call the methods with the doctrine $qb as an argument.
I have two entities in 1:n relations: Race and Day - a race may have more days. This is the simple model:
Race (id)
Day (id, race_id, is_active, is_deleted)
I want to access the superior one - Race - within a Symfony2 project via Doctrine and display results in a Twig template. For the direct Race attributes it is easy.
However, it becomes trickier, when I want to use a custom defined method (sort of flag, let's call it hasActiveDays()) in Race that reflects if the race had any active and not deleted days. Simple Doctrine relation would not be enough, so I need to use a query like this:
SELECT d FROM mtboLibBundle:Day d WHERE d.isActive = 1 AND d.isDeleted = 0 AND d.raceId = :id
My question is basicly where/how to implement this query and how to invoke it in a twig template? Anything I tried resulted various errors so far, so I'd be grateful if someone could help.
I.e. this was a try:
class RaceRepository extends EntityRepository {
public function hasActiveDays() {
$em = $this->getEntityManager();
$query = $em->createQuery('SELECT f FROM mtboLibBundle:Day d
WHERE d.isActive = 1
AND d.isDeleted = 0
AND d.raceId = :id')
->setParameter('id', $this->id)
;
$days = $query->getResult();
return (count($days) == 0) ? false : true;
}
}
Method does not exist - when called from the template:
{{ race.hasActiveDays }}
I don't think you'll be able to call a function in a repository the way you are trying to do. One thing I've done is put a function on the entity class and you can call that from your template.
In your Race class:
public function hasActiveDays(){
// here, perhaps pull all of the days for this race - maybe from a doctrine relation
// loop through, filter, etc.
// return whatever is appropriate
}
... then, in your template, you'll be able to call that function the way you are trying to above.