QueryBuilder with a relationship in Symfony - php

I'm coding in Symfony a search bar working with a query builder for an entity named "Structure" which is related to an entity "Partenaire" (OneToMany), it's working great but the problem is that it shows all the structures and I need to display only the structures related to the Partenaire. If someone can help me to solve this issue, thank you.
PartenaireController.php:
#[Route('/{id}', name: 'app_partenaire_show', methods: ['GET', 'POST'])]
public function show(Partenaire $partenaire, EntityManagerInterface $entityManager, PartenaireRepository $partenaireRepository,Request $request, PartenairePermissionRepository $partenairePermissionRepository, StructureRepository $structureRepository): Response
{
$getEmail = $this->getUser()->getEmail();
$partenaireId = $entityManager->getRepository(Partenaire::class)->findOneBy([
'id' => $request->get('id')
]);
$search2 = $structureRepository->findOneBySomeField2(
$request->query->get('q')
);
return $this->render('partenaire/show.html.twig', [
'partenaire' => $partenaire,
'permission'=>$partenairePermissionRepository->findBy(
['partenaire' => $partenaireId],
),
'structures'=>$search2, // show all the structures
/* 'structures'=>$structureRepository->findBy( // show the structure linked to the partenaire but doesn't work with the search
['partenaire' => $partenaireId],
[],
),*/
'email'=>$getEmail,
]);
}
StructureRepository.php :
public function findOneBySomeField2(string $search2 = null): array
{
$queryBuilder = $this->createQueryBuilder('q')
->orderBy('q.id' , 'ASC');
if ($search2) {
$queryBuilder->andWhere('q.Adresse LIKE :search')
->setParameter('search', '%'.$search2.'%');
}
return $queryBuilder->getQuery()
->getResult()
;
}

as it's a One To Many relation, you should join the right table and add a where statement in your structure query like :
public function findByPartenaireWithFilter(Partenaire $partenaire, string $search2 = null): array
{
$queryBuilder = $this->createQueryBuilder('q')
->leftJoin('q.partenaire','p')
->where('p.partenaire.id = :partenaireId')
->setParameter('partenaireId', $partenaire->getId())
->orderBy('q.id' , 'ASC');
if (null !== $search2) {
$queryBuilder->andWhere('q.Adresse LIKE :search')
->setParameter('search', '%'.$search2.'%');
}
return $queryBuilder
->getQuery()
->getResult();
}

You need to limit your results to the desired Partenaire by adding a condition. You can achieve this by simply passing another argument to the repository function.
I've removed the commented code in the controller and the loading of the Partenaire entity, as it should be already loaded thanks to the magic of param converters.
#[Route('/{id}', name: 'app_partenaire_show', methods: ['GET', 'POST'])]
public function show(Partenaire $partenaire, EntityManagerInterface $entityManager, Request $request, PartenairePermissionRepository $partenairePermissionRepository, StructureRepository $structureRepository): Response
{
$getEmail = $this->getUser()->getEmail();
// The Partenaire should already be loaded
$result = $structureRepository->findByPartenaireWithFilter(
$partenaire,
$request->query->get('q')
);
return $this->render('partenaire/show.html.twig', [
'partenaire' => $partenaire,
'permission' => $partenairePermissionRepository->findBy(
['partenaire' => $partenaireId],
),
'structures' => $search2, // show all the structures
'email' => $getEmail,
]);
}
As for the repository, it's pretty straightforward: pass the loaded partenaire and add a where clause.
public function findByPartenaireWithFilter(Partenaire $partenaire, string $search2 = null): array
{
$queryBuilder = $this->createQueryBuilder('q')
->where('q.partenaire = :partenaire')
->setParameter('partenaire', $partenaire)
->orderBy('q.id' , 'ASC');
if (null !== $search2) {
$queryBuilder->andWhere('q.Adresse LIKE :search')
->setParameter('search', '%'.$search2.'%');
}
return $queryBuilder
->getQuery()
->getResult();
}
As for the permissions key in the controller, I think what you are trying to do, I'd recommend looking into security voters.

Related

Problem with the function update() using Repository pattern to write REST API

I have a problem that all the create-read-delete using Repository Pattern is good but the update function is error. I still have the data but the information is not updated.
This is my code in EventController
public function update(EventRequest $request, $id)
{
$events = $this->repository->update($request->all());
return $this->sendResponse($events->toArray(), 'Successfully updated the Event!!');
}
This is i use DI for inject from the Repository, this is EventRepository.php
public function update($id, array $array) {
$events = $this->model->findOrFail($id);
$events->update($array);
return $events;
}
when i use dd($array) and the result returns [] without anything. Can anyone help me. Did i write anything wrong in this. Or i write the wrong Request
public function rules()
{
// $id = $this->events ? ',' . $this->events->id : '';
return $rules = [
'event_title' => 'required|max:255',
'event_type_id' => 'required|integer|between:1,3',
'from_date' => 'required|date_format:Y-m-d H:i:s',
'to_date' => 'date_format:Y-m-d H:i:s|nullable',
'is_recurring' => 'boolean|required',
'remarks' => 'nullable',
];
}
This method takes two arguments:
public function update($id, array $array) {
However, that's not how you are calling it:
$this->repository->update($request->all());
I take it $request->all() gives you an array, so pass the ID first.
$this->repository->update($id, $request->all());

research in the DB by the start of the name symfony

I would need a little help. I'm trying to implement a search bar with symfony, this search bar in the database. What I can do but the problem is that I absolutely have to put the full name to find the person (s) corresponds to that name. Except that I would like to be able to retrieve all the people starting with the beginning of the name I entered. For example :
I type "dub" and I find: "Dubois", "Dubost", "Dubububu", ....
public function searchAction (Request $request)
{
$defaultData = array('search' => 'Type your search here');
$form = $this->createFormBuilder()
->add('search', TextType::class, array('label'=>'Surname ?','required'=>true))
->add('send', SubmitType::class)
->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$data = $form->getData();
$repository = $this
->getDoctrine()
->getManager()
->getRepository('PegasusWebBundle:Clients')
;
//$clients = $repository->findBySurname($form->get('search')->getData()); // Method 1
$clients = $repository->myFindname($form->get('search')->getData()); // Method 2 with Repository
return $this->render('PegasusWebBundle:Default:searchresult.html.twig',array(
'clients'=> $clients ));
}
else
return $this->render('PegasusWebBundle:Default:search.html.twig', array(
'form' => $form->createView(),
));
}
The repository for the method 2
`class ClientsRepository extends \Doctrine\ORM\EntityRepository
{
public function myFindname($name)
{
// Method 2
$qb = $this->createQueryBuilder('a');
$qb->where(
$qb->expr()->like('a.surname' ,':surname'))
->setParameter('surname', $name);
return $qb
->getQuery()
->getResult();
}
}`
Try adding the wildchar eg:
$query = $repo->createQueryBuilder('a')
->where('a.surname LIKE :surname')
->setParameter('surname', '%'.$name.'%')
->getQuery();
Try adding a SQL wildcard character (%) after the surname value.
Also See this as reference.

How to combine multiple methods without repeating queries in Controller?

I have a method index
protected function index(Request $request)
{
$articles = Article::published()->paginate(8);
return view('pages.blog', [
'articles' => $articles,
'orientation' => $this->getOrientation(),
$this->getCategory($request)
]);
}
And a method getCategory()
public function getCategory($request)
{
if ($request->has('category')){
$search = $request->get('category');
$articles = Article::published()
->where('orientation', 'LIKE', '%' . $search . '%')
->paginate(8);
return view('pages.blog', [
'articles' => $articles,
'orientation' => $this->getOrientation()
]);
}
}
As you can see, I try to get my getCategory function outside of my index function.
It works, only, with my debug bar, I have all the SQL queries (those of index and those of getCategory).
Is it possible to optimize my code? Can you help me ?
Thank you
If you need both results add cache for query in index and get full result from cache.
Or if you have all results maybe use collections filter for same your collection from index and provide display data what you need.
For example getCategory add param where you set your collection from index. In article add filter to get only data what you are interest in.
I'm nowhere near an editor to chekc this, but here goes. First function:
public function index(Request $request)
{
$articles = Article::published()->paginate(8);
return view('pages.blog', [
'articles' => $articles,
'orientation' => $this->getOrientation(),
//$this->getCategory($request) - this i don't think can work like this. You are calling
//a function that does not return a value, but instead loads a view, insted:
'categories' => $this->getCategory($request)//the idea here is you call a function and
//get a return as an array
]);
}
Second function:
public function getCategory($request)
{
if ($request->has('category')){
$search = $request->get('category');
$articles = Article::published()
->where('orientation', 'LIKE', '%' . $search . '%')
->paginate(8);
$result = [
'articles' => $articles,
'orientation' => $this->getOrientation()
];
}
else $result = null;
return $result;
}
This might work, once again, im a noob. But i don't see why you need 2 methods for this since there is a if ($request->has('category')){ which will yield a null. Idk, give it a try...

Symfony submit a search form with categories and region

I got a search form in page's header that must to submit to my SearchController and be handeld by searchAction in case of the user input only the query string.
This form got the ability to search in selected category and country region.
So the user can make the search in all category and all region or select category and all region or select category and region
How I can handle this? I've made the controller action that actually work when i put the data in url.
Here is my search class using QueryBuilder:
class SearchController extends Controller
{
/**
* #Route("cerca/{query}", name="search")
*/
public function searchAction(Request $request,$query)
{
$results = $this->getDoctrine()
->getRepository('AppBundle:Ads')
->createQueryBuilder('p')
->where("p.name LIKE '%$query%'")
->getQuery()
->getResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);
return $this->render('search\search.html.twig', [
//'pagination'=>$pagination,
'query' => $query,
'region' => 'Italia',
'results' => $results,
]);
}
/**
* #Route("cerca/{region}/{query}", name="search_regioni")
*/
public function searchRegionAction($region,$query)
{
$results = $this->getDoctrine()
->getRepository('AppBundle:Ads')
->createQueryBuilder('p')
->where("p.name LIKE '%$query%'")
->andWhere("p.region Like '%$region%'")
->getQuery()
->getResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);
$region = ucfirst($region);
return $this->render('search/search.html.twig', [
'region' => $region,
'query' => $query,
]);
}
/**
* #Route("cerca/{category}/{query}", name="search_categorie")
*/
public function searchCategoryAction($category,$query)
{
$results = $this->getDoctrine()
->getRepository('AppBundle:Ads')
->createQueryBuilder('p')
->where("p.name LIKE '%$query%'")
->andWhere("p.region Like '%$region%'")
->getQuery()
->getResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);
$category = ucfirst($category);
return $this->render('search/search.html.twig', [
'region' => 'Italia',
'category' => $category,
'query' => $query,
]);
}
/**
* #Route("cerca/{category}/{region}/{query}", name="search_categorie_regioni")
*/
public function searchCategoryRegionAction($category,$region,$query)
{
$results = $this->getDoctrine()
->getRepository('AppBundle:Ads')
->createQueryBuilder('p')
->where("p.name LIKE '%$query%'")
->andWhere("p.region LIKE '%$region%'")
->andWhere("p.category LIKE '%$category%'")
->getQuery()
->getResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);
$category = ucfirst($category);
$region = ucfirst($region);
return $this->render('search/search.html.twig', [
'region' => $region,
'category' => $category,
'query' => $query,
]);
}
}
How I can make the form submit to this controller? How to handle the different cases?
I think my logic is wrong too, symfony don't know if I search for a category or country region, how can I improve this?
I think instead of having different actions you should submit the data and store it in a ValueObject, e.g. a Search object:
class Search {
public $query;
public $category;
public $region;
}
You could also create a SearchType that binds a form to the value object and provides some validation and constraints, e.g. create a ChoiceLoader that loads the available categories from a database and checks that the selected one is valid. After submitting and validating the form input you retrieve the search object from the form and use it to build your Doctrine-Query. In case region is empty, e.g. not selected you just omit it from the query like this:
/** #var Search $search **/
$search = $form->getData();
$queryBuilder = $this->getDoctrine()
->getRepository('AppBundle:Ads')
->createQueryBuilder('p')
$queryBuilder->where($queryBuilder->expr()->like('p.name', "%{$search->query}%");
if (null != $search->region) {
$queryBuilder->andWhere('region = :region')
->setParameter('region', $search->region);
}
$results = $queryBuilder->createQuery()->getResults();
Some things to note:
Do not just inject the variables into the where-clauses. This is a security problem. You should use parameters and the expression builder to safeguard against SQL injection.
You can chain clauses in the query builder (like you do above) or split them up, like I do in the if condition.
Since the form is already connected to the Search-object the fields should be pre-filled with the last inserted values, so you don't have to pass around the input separately.
I'm not 100% sure the LIKE-expression will work as expected. It can be somewhat tricky. You should search for how to use it properly.

Query at Sonata Admin

I'm using Sonata to make a backoffice and at configureFormFields, I want do a query to return some values. The query is well done and returns values when I use var_dump, but at the form I always get "Class does not exist". Can you help me?
Here is the code:
protected function configureFormFields(FormMapper $formMapper)
{
/* #var $queryBuilder \Doctrine\ORM\QueryBuilder */
$queryBuilder = $this->getModelManager()
->getEntityManager('EBCoreKernelBundle:Campaign\Campaign')
->createQueryBuilder();
$queryBuilder->select('cmp.id, cmp.name')
->from('EBCoreKernelBundle:Campaign\Campaign', 'cmp');
/* #var $templateList Template[] */
$templateList = $queryBuilder->getQuery()->execute();
var_dump($templateList);
$formMapper
->add('name','sonata_type_model', array('required' => true, 'query' => $queryBuilder));
}
$entity = new \Nnx\AbpBundle\Entity\Truc();
$query = $this->modelManager->getEntityManager($entity)->createQuery('SELECT t FROM Nnx\AbpBundle\Entity\Truc t ORDER BY t.lib ASC')->execute();
As documentation:
https://sonata-project.org/bundles/admin/master/doc/reference/form_types.html
query defaults to null. You can set this to a QueryBuilder instance in
order to define a custom query for retrieving the available options.
So, let's give it a query builder:
$queryBuilder = $this->getModelManager()
->getEntityManager(Category::class)
->createQueryBuilder('c')
->select('c')
->from('AppBundle:Category', 'c')
->orderBy('c.title', 'ASC')
;
$formMapper->add('toto', ModelType::class, array(
'query' => $queryBuilder
))

Categories