Share logic between queries in Doctrine - symfony

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.

Related

Return a value from entity to view file in symfony

I want to return a value from entity to view file. Below is my entity function
public function getVisitorName($id)
{
$repository = $this->getDoctrine()->getRepository('SystemVmsBundle:VisitorsDetails');
$product = $repository->findOneBy(array('id' =>$id));
$name=$product->getFirstname();
return $name;
}
This is the line in my view file which calls that function
{{ entity.visitorName(entity.visitorId) }}
Its not giving me any error. But only a blank page. How can i fix this?
This is my controller code
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('SystemVmsBundle:EntryDetails')->findAll();
return array(
'entities' => $entities,
);
}
I am trying to fetch the visitors name(from visitors table) corresponding to the visitor id(in entry table).How will i do it then?
you have two ways of doing it:
1) Map your SystemVmsBundle:EntryDetails entity, to SystemVmsBundle:VisitorsDetails as OntToOne by adding field details to your EntryDetails; , and then in twig template just call it via
{{ entity.details.name }}
2) instead of creating getVisitorName(), it is better to create twig function for this, with same functionality.
Your indexAction() is not returning a response object, it is just returning an array of entities. Controller actions should return a Response containing the html to be displayed (unless they are for e.g. ajax calls from javascript). If you are using twig templates you can use the controller render() method to create your response, something like this:
return $this->render('<YourBundle>:<YourViewsFolder>:<YourView>.html.twig', array(
'entities' => $entities,
));
When you've corrected that I suspect you'll get an error because $this->getDoctrine() won't work from an entity class. The code you have in the getVisitorName() method just shouldn't be in an entity class.
As #pomaxa has already suggested, I believe there should be a relationship between your EntryDetails and VisitorsDetails entities although I don't know enough about your data from the question to know what type of relationship it should be (OneToOne / ManyToOne). If your EntryDetails entity had a relationship to VisitorsDetails, the EntryDetails class would then contain a $visitorsDetails attribute and methods to get/set it. Then the line in your twig file would look like this:
{{ entity.visitorsDetails.firstName }}
There is a section on Entity Relationships / Associations in the Symfony Manual.
Also, I hope you don't mind me giving you a little advice:
Be careful when you copy and paste code as it appears you have done in getVisitorName(). You have kept the variable name '$product' although there are no products in your system. This sort of thing can cause bugs and make the code more difficult to maintain.
I recommend you avoid tacking 'Details' onto the end of entity names unless you genuinely have two separate and related entities such as Visitor + VisitorDetails and a good reason for doing so. I think the entities in your example are actually 'Visitor' and 'VistorEntry'.
Unless you are writing a re-usable component, I recommend you use specific variable names like '$visitorEntries' rather than '$entities' in your controller and twig.
In general, the more meaningful your variable names, the more readable, maintainable and bug-free your code is likely to be. Subsequently, it will also be much easier for people on SO to understand your code and give you help when you need it.

$em->remove() symfony2 erasing all rows

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.

Define custom method and access it from twig

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.

Symfony/Doctrine: fetching data as object , still get array

I have in my controller $id it's a foreign key
$query = $em->getRepository('SurgeryPatientBundle:Patients')->findPatientByUserID($id);
And in my repository file this function
public function findPatientByUserID($id)
{
return $this->getEntityManager()
->createQuery('SELECT p FROM SurgeryPatientBundle:Patients p WHERE p.user ='.$id.'')
->execute();
}
I want get an instance of object but still get an array. Query with find($id) works good
edit
Problem solves , I'm so stupid , I had invoked to $query[0]
You can use $query->getSingleResult(); as well
see here
http://docs.doctrine-project.org/en/2.1/reference/dql-doctrine-query-language.html#query-result-formats
If you want to grab the object, you shouldn't be using DQL. Doctrine entities have a find function that takes care of this for you.
Instead of all that code, you can just use (in your controller):
$em->getRepository('SurgeryPatientBundle:Patients')->find($id);
DQL is very powerful, but for simple lookups like this using the built in find methods will be more efficient & provide the entities as doctrine objects.

How to create a unique form using multiple entities fields in symfony2

I want to create a form using some fields from multiple entities. I have all the distinct entites needed already created and i am not using form classes. I need to know how to do to render a form and handle its data so i can save them to the correct tables in my database.
Here is a part of my controller in charge of doing that
public function createPublicSpaceAction() {
//My entities
$Room = new Room();
$GuestList = new GuestList();
$Guest = new Guest();
//I need to know what to do from here
return $this -> render('AcmeUserBundle:Default:Forms/createPublicSpace.html.twig', array());
}
I kept trying to find a solution and i came up with the idea that one form needs one entity. So maybe the solution would be to merge those entities in one so i can build the form easily. I would then have to persist data to corresponding tables. But i can't think of how to merge entities.
I figured out a temporary solution. For those who want to know, I manually created an entity that looks like a merge of all the entity I need. This new entity has no link with Doctrine therefore it cannot create a table. Its goal is simply to allow me to build up a form and be able to manipulate data through that form. I then assign all data submitted to corresponding entities fields and persist them to the database.
Once again i know this is not the best solution. But for some reasons I won't tell, it is for me at this moment. I hope this can help some that are in the same situation than me and do not hesitate to post links that could help or better ways to do that.
It is highly recommended to use form classes http://symfony.com/doc/current/book/forms.html#creating-form-classes
They are designed to save time and make a lot of things just easier.
However to answer your question consider the following. Your action needs to handel a post request. So catch the request object with the post data:
use Symfony\Component\HttpFoundation\Request;
public function createPublicSpaceAction(Request $request)
Then get a form builder intance and create the form:
$builder = $this->createFormBuilder();
$builder->add('floor', 'text', array(
'label' => 'Room floor',
'data' => $room->getFloor()
));
add as much form fields as you need. There are several built-in field types: http://symfony.com/doc/current/book/forms.html#built-in-field-types
Create the form:
$form = $builder->getForm();
Pass the form to your template:
return $this -> render('AcmeUserBundle:Default:Forms/
createPublicSpace.html.twig', array(
'roomForm' = $form
));
To get posted data within your action:
if ('POST' == $request->getMethod()) {
$data = $request->request->get("form");
}
And in your template you can render the form by yourself or let twig do the job:
{{ form_widget(form.floor)}}
So this are the most importend things to mention. However you should go through http://symfony.com/doc/current/book/forms.html They actually tell you everything I wrote down.
Good luck ;)

Resources