i want create a request from my EntityRepository so this is my code:
<?php
namespace zhr\myprojectBundle\Entity;
use Doctrine\ORM\EntityRepository;
/**
* myprojectdbEntityRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class myprojectdbEntityRepository extends EntityRepository
{
public function getAll()
{
$qb = $this->createQueryBuilder('s');
$query = $qb;
$result = $qb->getQuery()->execute();
}
}
i want use it in my controller file so this is my code:
public function searchusersAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$repository = $em->getRepository('myprojectBundle:User');
$utilisateur = $repository->getAll();
var_dump($utilisateur); die();
//return $this->render('mypageBundle:Admin:adminindex.html.twig', array('sheet'=>$utilisateur));
}
i get a error:
Undefined method 'getAll'. The method name must start with either findBy or findOneBy!
??? normaly all methode in repository file must be defined in my controller file no ?
thanks first guys
You have to use getResult() to return a result as follow:
public function getAll()
{
return $this->createQueryBuilder('s')->getQuery()->getResult();
}
But you really don't need this kind of function since EntityRepository class already has findAll() function to get all rows from an entity:
$em = $this->getDoctrine()->getManager();
$rows = $em->getRepository('myprojectBundle:User')->findAll();
I suggest you to check documentation for QueryBuilder and EntityRepository api.
Related
I'm trying to create an SQLFilter for a query in my Symfony app.
The issue is that the filter is not applied on the query (and not called), even though is it enabled correctly (see below).
The repository is not linked to an entity, because the database is external to my app, but it still has access to the data.
Am I missing something ?
Here's the filter:
<?php
namespace App\SQL\Filter;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query\Filter\SQLFilter;
class UserRoleFilter extends SQLFilter
{
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias)
{
return 'c.roleId = 1';
}
}
I registered it in config/packages/doctrine.yaml:
doctrine:
filters:
user_role: App\SQL\Filter\UserRoleFilter
The controller:
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Doctrine\Persistence\ManagerRegistry;
use App\Repository\CustomerRepository;
class CustomerController extends AbstractController
{
public function myAction(Request $request, ManagerRegistry $doctrine, CustomerRepository $customerRepository)
{
$doctrine->getManager()->getFilters()->enable('user_role');
$customers = $customerRepository->findAll();
}
}
The repository:
<?php
namespace App\Repository;
use Doctrine\DBAL\Connection;
use Doctrine\Persistence\ManagerRegistry;
use Doctrine\Persistence\ObjectManager;
class CustomerRepository
{
protected Connection $conn;
protected ObjectManager $em;
public function __construct(ManagerRegistry $doctrine)
{
$this->em = $doctrine->getManager();
$this->conn = $this->em->getConnection();
}
public function findAll(): array
{
dump($this->em->getFilters()->isEnabled('user_role')); // returns true
return $this->conn->createQueryBuilder()
->select('c.*')
->from('customer', 'c')
->executeQuery()
->fetchAllAssociative();
}
}
From looking at the source for Doctrine/DBAL, it doesn't look like the filter would ever be applied to the query you are executing.
Within your repository class you are creating an instance of Doctrine\DBAL\Query\QueryBuilder which only holds a reference to Doctrine\DBAL\Connection.
Then the select data is set to its private parameter $sqlParts.
When executeQuery() is called is concatenates the content of sqlParts into a string with no mention or reference to any filter objects nor their constraints. This can be seen on line 308 of the QueryBuilder class.
QueryBuilder::executeQuery()
You can also see how the select query is concatenated on line 1320 of QueryBuilder.
QueryBuilder::getSQLForSelect()
The only way I can see to add it easily would be to add it directly to a where clause, e.g.
public function findAll(): array
{
return $this->conn->createQueryBuilder()
->select('c.*')
->from('customer', 'c')
->where("c.roleId = 1") // Or pull it from the filter object in some way
->executeQuery()
->fetchAllAssociative();
}
If you want to see where the filter constraints are added to the queries you can find that data in the ORM package from Doctrine, however these are all linked to entities and table aliases.
SqlWalker::generateFilterConditionSQL()
BasicEntityPersistor::generateFilterConditionSQL()
ManyToManyPersistor::generateFilterConditionSQL()
I am following a tutorial to write 2 classes for filtering threads in a forum application.I got this error in line
$threads = Thread::latest()->filter($filters); // in threadscontroller
Error:
Method Illuminate\Database\Query\Builder::filter does not exist.
ThreadsController with index method:
<?php
namespace App\Http\Controllers;
use App\Thread;
use App\Channel;
use App\Filters\ThreadFilters;
use Illuminate\Http\Request;
class ThreadsController extends Controller
{
public function __construct(){
$this->middleware('auth')->only('store','create');
}
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index(Channel $channel,ThreadFilters $filters)
{
$threads = Thread::latest()->filter($filters);
if($channel->exist){
$threads->where('channel_id',$channel->id);
}
$threads = $threads->get();
return view('threads.index',compact('threads'));
}
This is the abstract class Filters:
<?php
namespace App\Filters;
use Illuminate\Http\Request;
abstract class Filters{
protected $request;
protected $builder;
protected $filters = [];
public function __construct(Request $request){
$this->request = $request;
}
public function apply($builder){
$this->builder = $builder;
foreach($this->getFilters() as $filter=>$value){ //filter by,value yunus mesela.
if(method_exist($this,$filter)){
$this->$filter($value);
}
}
return $this->builder;
}
public function getFilters(){
return $this->request->intersect($this->filters);
}
}
Here ThreadFilters.php which extends filters class:
<?php
namespace App\Filters;
use App\User;
use Illuminate\Http\Request;
class ThreadFilters extends Filters
{
protected $filters =['by'];
protected function by($username){
$user = User::where('name',$username)->firstorFail();
return $this->builder->where('user_id',$user->id);
}
}
If I change latest to all, I get this error:
Type error: Argument 1 passed to
Illuminate\Support\Collection::filter() must be callable or null,
object given, called in
Also can anyone explain me what is $builder doing in those classes?
latest() is a modifier shortcut, equivalent to orderBy('created_at', 'desc'). All it does is add the ORDER BY constraint to the query.
filter() is a method on the Collection class. That method does not exist in the query builder, hence the "method not found" error you're receiving.
It does not appear that your filter class should be used with the resulting Collection. Rather, it adds conditionals to your original query. Try implementing it like this:
// Remove the filters() method here.
$threads = Thread::latest();
if ($channel->exist) {
$threads->where('channel_id', $channel->id);
}
// Pass your query builder instance to the Filters' apply() method.
$filters->apply($threads);
// Perform the query and fetch results.
$threads = $threads->get();
Also, for future questions, including the tutorial you're attempting/following can provide beneficial context to those helping you. :)
If you change latest to all, you're getting a Laravel Collection. So you are calling filter() on a Collection ($threads = Thread::all()->filter($filters);).
If you take a look into the code, you'll see, that the where() method of the array class gets called, which calls PHP's array_filter method. As you can see, a callable must be given.
But you are passing an Object to the filter method, $filters, which is an ThreadFilters-Object -> method injection here:
public function index(Channel $channel,ThreadFilters $filters) ...
Your error message answers your question in a great way:
Type error: Argument 1 passed to Illuminate\Support\Collection::filter() must be callable or null, object given, called in
I have been creating web application using symfony 3.4, I want to use EntityMerger inside PATCH method in the controller.
When I inject the EntityMerger in the constructor of the controller it takes the value null in the function patchMovieAction.
namespace AppBundle\Entity;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\ORM\Mapping\Id;
class EntityMerger
{
private $annotationReader;
public function __constructor(AnnotationReader $annotationReader)
{
$this->annotationReader = $annotationReader;
}
public function merge($entity, $changes): void
{
....
}
}
the Controller:
class MoviesController extends AbstractController
{
use ControllerTrait;
private $entityMerger;
public function __constructor(EntityMerger $entityMerger)
{
$this->entityMerger = $entityMerger;
}
public function patchMovieAction(Movie $movie, Movie $modifiedMovie, ConstraintViolationListInterface $validationErrors)
{
if(null ===$movie) {
return $this->view(null,404);
}
if(count($validationErrors)>0){
throw new ValidationException($validationErrors);
}
//Merge entities
$this->entityMerger->merge($movie,$modifiedMovie);
//Persist
$em = $this->getDoctrine()->getManager();
$em->persist($movie);
$em->flush();
//Return
return $movie;
}
When I try to run php bin/console debug:router, I get the error
Cannot autowire service "AppBundle\Controller\MoviesController": argument "$entityMerger" of method "__construct()" references class "AppBundle\Entity\EntityMerger" but
no such service exists.
I want to replace the Laravels builder class with my own that's extending from it. I thought it would be as simple as matter of App::bind but it seems that does not work. Where should I place the binding and what is the proper way to do that in Laravel?
This is what I have tried:
my Builder:
use Illuminate\Database\Eloquent\Builder as BaseBuilder;
class Builder extends BaseBuilder
{
/**
* Find a model by its primary key.
*
* #param mixed $id
* #param array $columns
* #return \Illuminate\Database\Eloquent\Model|static|null
*/
public function find($id, $columns = array('*'))
{
Event::fire('before.find', array($this));
$result = parent::find($id, $columns);
Event::fire('after.find', array($this));
return $result;
}
}
And next I tried to register the binding in bootstrap/start.php file like this :
$app->bind('Illuminate\\Database\\Eloquent\\Builder', 'MyNameSpace\\Database\\Eloquent\\Builder');
return $app;
Illuminate\Database\Eloquent\Builder class is an internal class and as such it is not dependency injected into the Illuminate\Database\Eloquent\Model class, but kind of hard coded there.
To do what you want to do, I would extend the Illuminate\Database\Eloquent\Model to MyNamespace\Database\Eloquent\Model class and override newEloquentBuilder function.
public function newEloquentBuilder($query)
{
return new MyNamespace\Database\Eloquent\Builder($query);
}
Then alias MyNamespace\Database\Eloquent\Model to Eloquent at the aliases in app/config/app.php
Both of the answers are correct in some way. You have to decide what your goal is.
Change Eloquent Builder
For example, if you want to add a new method only for eloquent models (eg. something like scopes, but maybe a little more advanced so it’s not possible in a scope)
Create a new Class extending the Eloquent Builder, for Example CustomEloquentBuilder.
use Illuminate\Database\Eloquent\Builder;
class CustomEloquentBuilder extends Builder
{
public function myMethod()
{
// some method things
}
}
Create a Custom Model and overwrite the method newEloquentBuilder
use Namespace\Of\CustomEloquentBuilder;
use Illuminate\Database\Eloquent\Model;
class CustomModel extends Model
{
public function newEloquentBuilder($query)
{
return new CustomEloquentBuilder($query);
}
}
Change Database Query Builder
For example to modify the where-clause for all database accesses
Create a new Class extending the Database Builder, for Example CustomQueryBuilder.
use Illuminate\Database\Query\Builder;
class CustomQueryBuilder extends Builder
{
public function myMethod()
{
// some method things
}
}
Create a Custom Model and overwrite the method newBaseQueryBuilder
use Namespace\Of\CustomQueryBuilder;
use Illuminate\Database\Eloquent\Model;
class CustomModel extends Model
{
protected function newBaseQueryBuilder()
{
$connection = $this->getConnection();
return new CustomQueryBuilder(
$connection, $connection->getQueryGrammar(), $connection->getPostProcessor()
);
}
}
Laravel Version: 5.5 / this code is untestet
The answer above doesn't exactly work for laravel > 5 so I done some digging and I found this!
https://github.com/laravel/framework/blob/5.2/src/Illuminate/Database/Eloquent/Model.php#L1868
use this instead!
protected function newBaseQueryBuilder()
{
$conn = $this->getConnection();
$grammar = $conn->getQueryGrammar();
return new QueryBuilder($conn, $grammar, $conn->getPostProcessor());
}
In a bundle I'm developing and was working correctly I added a new functionality which involves adding a repository to the entity. Now, when I execute the newly added method I get the following error:
Warning: class_parents() [function.class-parents]: Class CmsPages does not exist and could not be loaded in /Applications/MAMP/htdocs/symfony-standard-2.1/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/RuntimeReflectionService.php line 40
The newly added code is:
Controller:
/**
* Returns an json formated tree
*
* #Route("/getTree", name="admin_cmsPages_getTree", options={"expose"=true})
*/
public function getTreeAction()
{
$em = $this->getDoctrine()->getManager();
$tree = $em->getRepository('CmsPages')->loadTree();
$response = new Response(json_encode( $tree ));
$response->headers->set('Content-Type', 'application/json');
return $response;
}
Repository:
namespace Yanic\CmsBundle\Entity;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\NoResultException;
class CmsPagesRepository extends EntityRepository
{
public function loadTree()
{
$q = $this
->createQueryBuilder('p')
->select('p')
->orderBy( 'p.lft' )
->getQuery()
;
return $q->getArrayResult();
}
}
That's all that has changed... if any more code is needed for clarification I will post it.
So could anybody tell me what I'm doing wrong? I couldn't find anything neither on SO nor on Google.
Thanks in advance
I just found the error myself... the line
$tree = $em->getRepository('CmsPages')->loadTree();
has to be
$tree = $em->getRepository('CmsBundle:CmsPages')->loadTree();