Symfony3 - create join query of two tables in controller - php

How to show two tables data from my controller.
Here is my controller's code.
class TestController extends Controller
{
public function showAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$teacher = $this->getDoctrine()->getRepository(Teacher::class);
$query = $em
->createQueryBuilder('t')
->from('AppBundle:Teacher','t')
->Join('AppBundle:Student','s')
->where('t.id=id and s.tid=tid')
->getQuery()
->getResult();
}
}
When print_r it's showing only one table data.
Please help

Please check below mentioned solution.
$query = $em
->createQueryBuilder('t.*,s.*')
->from('AppBundle:Teacher','t')
->Join('AppBundle:Student','s')
->where('t.id=id and s.tid=tid')
->getQuery()
->getResult();
}
Let me know if it not works.

I assume that you have defined a relationship between Teacher and Student in your entities. In this case you can get the Student objects by calling $teacher->getStudents() (assuming that you have defined such a method in your Teacher entity class). See Doctrine documentation about association mapping
Example for a One-To-Many relationship:
<?php
use Doctrine\Common\Collections\ArrayCollection;
/** #Entity */
class Teacher
{
// ...
/**
* One Teacher has Many Students.
* #OneToMany(targetEntity="Student", mappedBy="teacher")
*/
private $students;
// ...
public function __construct() {
$this->students = new ArrayCollection();
}
}
/** #Entity */
class Student
{
// ...
/**
* Many Students have One Teacher.
* #ManyToOne(targetEntity="Teacher", inversedBy="students")
* #JoinColumn(name="teacher_id", referencedColumnName="id")
*/
private $teacher;
// ...
}
In the QueryBuilder object you can avoid the need of additional queries on $teacher->getStudents() calls by adding something like that:
$query = $em
->createQueryBuilder('t')
->from('AppBundle:Teacher','t')
->join('AppBundle:Student','s')
->select(array('t', 's'))
->where('t.id=id and s.tid=tid')
->getQuery()
->getResult();
}
If there is a relationship defined between Teacher and Student in your entities as mentioned above you can even simplify the join:
$query = $em
->createQueryBuilder('t')
->from('AppBundle:Teacher','t')
->join('t.students', 's')
->select(array('t', 's'))
->getQuery()
->getResult();
}
Furthmore you do not need to call the from() method if you create the QueryBuilder object via the TeacherRepository object:
$query = $teacher
->createQueryBuilder('t')
->join('t.students', 's')
->select(array('t', 's'))
->getQuery()
->getResult();
}

$query = $em
->createQueryBuilder('t')
->add('select', 't,s')
->from('AppBundle:Teacher', 't')
->Join('AppBundle:Student', 's')
->where('t.id = s.tid')
->getQuery()
->getResult();
it working perfect.

First we select all from Teachers table, then join students. Assume that your relationship name in Teachers model is student. In repository file:
public function getWithStudents() {
return $this->createQueryBuilder('t')
->Join('t.student', 's')
->addSelect('s')
->getQuery()->getArrayResult();
}
Then in controller call it:
$teachersWithStudents = $teacher->getWithStudents();
Or in this case you can just
$teachersWithStudents = $teacher->getStudents();

Suppose you have two tables.comment table and article table and You want to fetch comments on each article
$commentContent = $em
// automatically knows to select Comment
// the "c" is an alias you'll use in the rest of the query
->createQueryBuilder('c')
->select('c.message, c.name')////Fields required to display
->from('AppBundle:Comment','c')
->join('AppBundle:Article','a')
->where('c.idArticle=a.id and c.publish_mainPage = 1')
->orderBy('c.idArticle', 'DESC')
->getQuery()
->getResult();
var_dump($commentContent);

Related

ApiPlatform - custom action with dbal query

Maybe I don't understand this idea but I'm starting create my first test app that use api platform.
I have custom action for my search:
/**
* #Route(
* path="/api/pharmacies/search",
* name="pharmacy_search",
* defaults={
* "_api_resource_class"=Pharmacy::class,
* "_api_collection_operation_name"="search"
* }
* )
* #param Request $request
*
* #return Paginator
*/
public function search(Request $request)
{
$query = SearchQuery::createFromRequest($request);
return $this->pharmacyRepository->search($query);
}
and method in the repository:
public function search(SearchQuery $searchQuery: Paginator
{
$firstResult = ($searchQuery->getPage() - 1) * self::ITEMS_PER_PAGE;
$qb = $this->createQueryBuilder('p')
/...
->setFirstResult($firstResult)
->setMaxResults(self::ITEMS_PER_PAGE);
$doctrinePaginator = new DoctrinePaginator($qb, false);
return new Paginator($doctrinePaginator);
}
And it works fine but this action doesn't need all field form the entity and relations to other tables. Currently this action creates 22 queries. I'd like create query in the DBAL/QueryBuilder and return pagination object with DTO.
public function search(SearchQuery $searchQuery)
{
.../
$qb = $this->createQueryBuilder('p')
->select('p.id')
->addSelect('p.name')/....
$rows = $qb->getQuery()->getArrayResult();
foreach ($rows as $row){
$data[] = new SearchPharmacy($row['id'], $row['name']);
}
return $data;
}
The above code will work but if the response isn't Pagination object and I don't have hydra:totalItems, hydra:next etc in the response.
In theory I can use DataTransformer and transform entity to DTO, but this way can't allow simplify database queries.
Can I achieve this?
I don't know how to use dbal query and mapped the result on a DTO, but I know how to add custom select, return it like a scalar:
public function search(SearchQuery $searchQuery): Paginator
{
$firstResult = ($searchQuery->getPage() - 1) * self::ITEMS_PER_PAGE;
$qb = $this->createQueryBuilder('p')
->select('p.id')
->addSelect('p.name')
->where('p.name LIKE :search')
->setParameter('search', "%" . $searchQuery->getSearch() . "%");
$query = $qb->getQuery()->setFirstResult($firstResult)
->setMaxResults(self::ITEMS_PER_PAGE);
$doctrinePaginator = new DoctrinePaginator($query, false);
$doctrinePaginator->setUseOutputWalkers(false);
return new Paginator($doctrinePaginator);
}
It finally I have 2 light queries, selected field from entity and Pagination.
Sources:
https://github.com/APY/APYDataGridBundle/issues/931
https://www.doctrine-project.org/projects/doctrine-orm/en/2.8/cookbook/dql-custom-walkers.html

Get the last value of a entity field with condition

I'm making a website for performance monitoring.
I have 2 tables:
Users table linked in one-to-Many with performances
Performances table linked in Many-to-one with users table
I just wanna get the last weight which is not null in my table performances and display it in twig
database screenshot
For exemple in this database, the result would be : 80
I tried with queries but I get an arror message so I don't know how to do
Thanks in advance for your help !
I hope this could helps you.
I suppose you have a repository class for your Performance entity. I would use this kind of code for a Symfony3 application
<?php
namespace AppBundle\Repository; //replace AppBundle by the name of your bundle
use Doctrine\ORM\Tools\Pagination\Paginator;
/**
* PerformanceRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class PerformanceRepository extends \Doctrine\ORM\EntityRepository
{
public function getLastWeigth()
{
$qb = $this->getEntityManager()->createQueryBuilder()
->select('p')
->from($this->_entityName, 'p')
->expr()->isNotNull('p.weight')
->orderBy('p.date', 'desc')
->setMaxResults(1);
$query = $qb->getQuery();
$result = $query->getSingleResult();
return $result;
}
}
Edit: here is an Exemple of use in the controller in a Symfony 3 application:
<?php
namespace AppBundle\Controller\Performance;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use AppBundle\Repository\PerformanceRepository;
class PerformanceController extends Controller
{
public function lastWeightAction()
{
$repo = $this->getDoctrine()->getManager()->getRepository('AppBundle:Performance');
$lastPerformance = $repo->getLastWeigth();
//some other code
}
}
Edit2:
If you need to get the last weight by a user Id:
class PerformanceRepository extends \Doctrine\ORM\EntityRepository
{
public function getLastWeigth($userId)
{
$qb = $this->getEntityManager()->createQueryBuilder()
->select('p')
->from($this->_entityName, 'p')
->join('p.user', 'u')
->where('u.id = :userId')
->expr()->isNotNull('p.weight')
->orderBy('p.date', 'desc')
->setMaxResults(1);
->setParameter(':userId', $userId);
$query = $qb->getQuery();
$result = $query->getSingleResult();
return $result;
}
}

Attempted to call method "createQueryBuilder" on class

I have an entity "Vehicules" that have ManyToOne relation with the entity "User". So each User hav one or more vehicules. I'm trying to count the number of vehicules foreach user and trying to show it in a table .
this is a part of my entity Vehicule
/**
* #ORM\ManyToOne(targetEntity="OC\UserBundle\Entity\User")
* #ORM\JoinColumn(name="User_id", referencedColumnName="id", onDelete="CASCADE")
*/
protected $direction;
this is the function in which i want to count the number of vehicules foreach user (direction) and show it in a table
public function afficheAction() {
$em = $this->getDoctrine();
$demandes = $em->getRepository('DemandevehBundle:Demande')->findAll();
// trying to count the number of vehicules foreach direction
$vehicules = $this->getDoctrine()->getRepository('CarPfeBundle:Vehicule')->findAll();
foreach($vehicules as $vehicule) {
$qb = $vehicule->createQueryBuilder('v');
$qb->select('count(v.id)');
$qb->where('v.direction = ?1');
$qb->setParameter('1', $vehicule->getId());
$query = $qb->getQuery();
$result = $query->getSingleScalarResult();
return $result;
}
// fin
return $this->render('DemandevehBundle:Demande:affiche.html.twig', array('demandes' => $demandes
,'result' => $result));
}
I get this error
Attempted to call method "createQueryBuilder" on class "Car\PfeBundle\Entity\Vehicule".
I feel that my function make no sense that's why i get this error. Any help please ?
Is possible to create a query builder on an entity manager instead of an entity. So you use the relative manager of the class so try this:
$qb = $this->getDoctrine()
->getRepository('CarPfeBundle:Vehicule')
->createQueryBuilder('v');
instead of this:
$qb = $vehicule->createQueryBuilder('v');
Hope this help

Laravel where on relationship object

I'm developing a web API with Laravel 5.0 but I'm not sure about a specific query I'm trying to build.
My classes are as follows:
class Event extends Model {
protected $table = 'events';
public $timestamps = false;
public function participants()
{
return $this->hasMany('App\Participant', 'IDEvent', 'ID');
}
public function owner()
{
return $this->hasOne('App\User', 'ID', 'IDOwner');
}
}
and
class Participant extends Model {
protected $table = 'participants';
public $timestamps = false;
public function user()
{
return $this->belongTo('App\User', 'IDUser', 'ID');
}
public function event()
{
return $this->belongTo('App\Event', 'IDEvent', 'ID');
}
}
Now, I want to get all the events with a specific participant.
I tried with:
Event::with('participants')->where('IDUser', 1)->get();
but the where condition is applied on the Event and not on its Participants. The following gives me an exception:
Participant::where('IDUser', 1)->event()->get();
I know that I can write this:
$list = Participant::where('IDUser', 1)->get();
for($item in $list) {
$event = $item->event;
// ... other code ...
}
but it doesn't seem very efficient to send so many queries to the server.
What is the best way to perform a where through a model relationship using Laravel 5 and Eloquent?
The correct syntax to do this on your relations is:
Event::whereHas('participants', function ($query) {
return $query->where('IDUser', '=', 1);
})->get();
This will return Events where Participants have a user ID of 1. If the Participant doesn't have a user ID of 1, the Event will NOT be returned.
Read more at https://laravel.com/docs/5.8/eloquent-relationships#eager-loading
#Cermbo's answer is not related to this question. In that answer, Laravel will give you all Events if each Event has 'participants' with IdUser of 1.
But if you want to get all Events with all 'participants' provided that all 'participants' have a IdUser of 1, then you should do something like this :
Event::with(["participants" => function($q){
$q->where('participants.IdUser', '=', 1);
}])
N.B:
In where use your table name, not Model name.
for laravel 8.57+
Event::whereRelation('participants', 'IDUser', '=', 1)->get();
With multiple joins, use something like this code:
$userId = 44;
Event::with(["owner", "participants" => function($q) use($userId ){
$q->where('participants.IdUser', '=', 1);
//$q->where('some other field', $userId );
}])
Use this code:
return Deal::with(["redeem" => function($q){
$q->where('user_id', '=', 1);
}])->get();
for laravel 8 use this instead
Event::whereHas('participants', function ($query) {
$query->where('user_id', '=', 1);
})->get();
this will return events that only with partcipats with user id 1 with that event relastionship,
I created a custom query scope in BaseModel (my all models extends this class):
/**
* Add a relationship exists condition (BelongsTo).
*
* #param Builder $query
* #param string|Model $relation Relation string name or you can try pass directly model and method will try guess relationship
* #param mixed $modelOrKey
* #return Builder|static
*/
public function scopeWhereHasRelated(Builder $query, $relation, $modelOrKey = null)
{
if ($relation instanceof Model && $modelOrKey === null) {
$modelOrKey = $relation;
$relation = Str::camel(class_basename($relation));
}
return $query->whereHas($relation, static function (Builder $query) use ($modelOrKey) {
return $query->whereKey($modelOrKey instanceof Model ? $modelOrKey->getKey() : $modelOrKey);
});
}
You can use it in many contexts for example:
Event::whereHasRelated('participants', 1)->isNotEmpty(); // where has participant with id = 1
Furthermore, you can try to omit relationship name and pass just model:
$participant = Participant::find(1);
Event::whereHasRelated($participant)->first(); // guess relationship based on class name and get id from model instance
[OOT]
A bit OOT, but this question is the most closest topic with my question.
Here is an example if you want to show Event where ALL participant meet certain requirement. Let's say, event where ALL the participant has fully paid. So, it WILL NOT return events which having one or more participants that haven't fully paid .
Simply use the whereDoesntHave of the others 2 statuses.
Let's say the statuses are haven't paid at all [eq:1], paid some of it [eq:2], and fully paid [eq:3]
Event::whereDoesntHave('participants', function ($query) {
return $query->whereRaw('payment = 1 or payment = 2');
})->get();
Tested on Laravel 5.8 - 7.x

Symfony2: how to get user's list by ROLE in FOSUserBundle? [duplicate]

I am using the FOS bundle and I want to retrieve all users with a given ROLE from the database.
What is the best way to do this?
Just add this in your UserRepository or replace $this->_entityName by YourUserBundle:User:
/**
* #param string $role
*
* #return array
*/
public function findByRole($role)
{
$qb = $this->_em->createQueryBuilder();
$qb->select('u')
->from($this->_entityName, 'u')
->where('u.roles LIKE :roles')
->setParameter('roles', '%"'.$role.'"%');
return $qb->getQuery()->getResult();
}
If you are using FOSUser Groups you should use:
/**
* #param string $role
*
* #return array
*/
public function findByRole($role)
{
$qb = $this->_em->createQueryBuilder();
$qb->select('u')
->from($this->_entityName, 'u')
->leftJoin('u.groups', 'g')
->where($qb->expr()->orX(
$qb->expr()->like('u.roles', ':roles'),
$qb->expr()->like('g.roles', ':roles')
))
->setParameter('roles', '%"'.$role.'"%');
return $qb->getQuery()->getResult();
}
Well, if there is no better solution, I think I will go to a DQL query:
$query = $this->getDoctrine()->getEntityManager()
->createQuery(
'SELECT u FROM MyBundle:User u WHERE u.roles LIKE :role'
)->setParameter('role', '%"ROLE_MY_ADMIN"%');
$users = $query->getResult();
If you have this requirement and your user list will be extensive, you will have problems with performance. I think you should not store the roles in a field as a serialized array. You should create an entity roles and many to many relationship with the users table.
As #Tirithen states, the problem is that you will not get the users that have an implicit role due to role hierarchy. But there is a way to work around that!
The Symfony security component provides a service that gives us all child roles for a specific parent roles. We can create a service that does almost the same thing, only it gives us all parent roles for a given child role.
Create a new service:
namespace Foo\BarBundle\Role;
use Symfony\Component\Security\Core\Role\RoleHierarchy;
use Symfony\Component\Security\Core\Role\Role;
/**
* ReversedRoleHierarchy defines a reversed role hierarchy.
*/
class ReversedRoleHierarchy extends RoleHierarchy
{
/**
* Constructor.
*
* #param array $hierarchy An array defining the hierarchy
*/
public function __construct(array $hierarchy)
{
// Reverse the role hierarchy.
$reversed = [];
foreach ($hierarchy as $main => $roles) {
foreach ($roles as $role) {
$reversed[$role][] = $main;
}
}
// Use the original algorithm to build the role map.
parent::__construct($reversed);
}
/**
* Helper function to get an array of strings
*
* #param array $roleNames An array of string role names
*
* #return array An array of string role names
*/
public function getParentRoles(array $roleNames)
{
$roles = [];
foreach ($roleNames as $roleName) {
$roles[] = new Role($roleName);
}
$results = [];
foreach ($this->getReachableRoles($roles) as $parent) {
$results[] = $parent->getRole();
}
return $results;
}
}
Define your service for instance in yaml and inject the role hierarchy into it:
# Provide a service that gives you all parent roles for a given role.
foo.bar.reversed_role_hierarchy:
class: Foo\BarBundle\Role\ReversedRoleHierarchy
arguments: ["%security.role_hierarchy.roles%"]
Now you are ready to use the class in your own service. By calling $injectedService->getParentRoles(['ROLE_YOUR_ROLE']); you will get an array containing all parent roles that will lead to the 'ROLE_YOUR_ROLE' permission. Query for users that have one or more of those roles... profit!
For instance, when you use MongoDB you can add a method to your user document repository:
/**
* Find all users with a specific role.
*/
public function fetchByRoles($roles = [])
{
return $this->createQueryBuilder('u')
->field('roles')->in($roles)
->sort('email', 'asc');
}
I'm not into Doctrine ORM but I'm sure it won't be so different.
You can use just this on your DQL:
SELECT u FROM YourFavouriteBundle:User u WHERE u.roles [NOT] LIKE '%ROLE_YOUR_ROLE%'
Of course with QueryBuilder it's more elegant:
// $role = 'ROLE_YOUR_ROLE';
$qb->where('u.roles [NOT] LIKE :role')
->setParameter('role', "%$role%");
Finally i solved it, following is an exact solution:
public function searchUsers($formData)
{
$em = $this->getEntityManager();
$usersRepository = $em->getRepository('ModelBundle:User');
$qb = $usersRepository->createQueryBuilder('r');
foreach ($formData as $field => $value) {
if($field == "roles"){
$qb->andWhere(":value_$field MEMBER OF r.roles")->setParameter("value_$field", $value);
}else{
$qb->andWhere("r.$field = :value_$field")->setParameter("value_$field", $value);
}
}
return $qb->getQuery()->getResult();
}
Cheers!
In case you need to filter users by role using a DQL filter in a YAML file (In EasyAdminBundle for instance)
entities:
Admin:
class: App\Entity\User
list:
dql_filter: "entity.roles LIKE '%%ROLE_ADMIN%%'"
Here I give an alternative solution :
I find users of roles for a given array
In controller I call the function like that
$users = $userRepository->findUsersOfRoles(['ROLE_ADMIN', 'ROLE_SUPER_USER']);
Then in my repository I make a loop to generate condition and set the parameters :
public function findUsersOfRoles($roles)
{
$condition = 'u.roles LIKE :roles0';
foreach ($roles as $key => $role){
if ($key !== 0){
$condition .= " OR u.roles LIKE :roles".$key;
}
}
$query = $this->createQueryBuilder('u')
->where($condition);
foreach ($roles as $key => $role){
$query ->setParameter('roles'.$key, '%"'.$role.'"%');
}
return $query->getQuery() ->getResult();
}

Categories