Apigility + Doctrine2 QueryProvider - Can't use created function on query builder - php

I'm using Apigility to create a rest application, where the back-end and front-end are pretty much independent applications.
Ok, on the Back-end I'm using 'zf-apigility-doctrine-query-provider' to create queries depending on the parameters sent via url (i.e localhost?instancia=10), but I need to process information using a MS SQL database stored function, something like this:
function createQuery(ResourceEvent $event, $entityClass, $parameters){
/* #var $queryBuilder \Doctrine\ORM\QueryBuilder */
$queryBuilder = parent::createQuery($event,$entityClass, $parameters);
if (!empty($parameters['instancia'])) {
$queryBuilder->andWhere($queryBuilder->expr()->eq('chapa.instancia', 'dbo.isItSpecial(:instancia)'))
->setParameter('instancia', $parameters['instancia']);
}
return $queryBuilder;
}
However it simply won't work, it won't accept the 'dbo.isItSpecial' and seems like I can't access the ServiceLocator, nor the EntityManager or anything but the Querybuilder.
I thought about creating a native query to get the result and the using it on the main query but seems like I can't create it.
Any ideas?

In what class is this method? Add some context to your question.
You do parent::createQuery which suggests that you are in a DoctrineResource instance. If this is true it means that both the ServiceLocator and the ObjectManager are simply available in the class.
You can read on doing native queries in Doctrine here in the Documentation:
$rsm = new ResultSetMapping();
$query = $entityManager->createNativeQuery(
'SELECT id, name, discr FROM users WHERE name = ?',
$rsm
);
$query->setParameter(1, 'romanb');
$users = $query->getResult();

Turns out I've found some ways to do this.
The class that the method was, extends this class
ZF\Apigility\Doctrine\Server\Query\Provider\DefaultOrm
That means I have access to the ObjectManager. The Documentation doesn't help much, but the ObjectManager is actually an EntityManager (ObjectManager is just the interface), to discover this I had to use the get_class PHP command.
With the entity manager I could have done what Wilt sugested, something like this:
$sqlNativa = $this->getObjectManager()->createNativeQuery("Select dbo.isItSpecial(:codInstancia) as codEleicao", $rsm);
However, I created a service that execute this function (it will be used in many places), so I also made a factory that set this service to the query provider class, on the configuration file it's something like this.
'zf-apigility-doctrine-query-provider' => array(
'factories' => array(
'instanciaDefaultQuery' => 'Api\Instancia\instanciaQueryFactory',
),
),
And the factory looks like something like this (the service is executing the NativeQuery like the response from Wilt):
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Api\EleitoChapaOrgao\EleitoChapaOrgaoQuery;
class InstanciaQueryFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceManager){
$instanciaService = $serviceManager->getServiceLocator()->get('Application\Service\Instancia');
$query = new InstanciaQuery($instanciaService);
return $query;
}
}
Finally just add the constructor to the QueryProvider and the service will be avaliable there:
class InstanciaQuery extends DefaultOrm
{
protected $instanciaService;
public function __construct(Instancia $instanciaService)
{
$this->instanciaService = $instanciaService;
}
public function createQuery(ResourceEvent $event, $entityClass, $parameters)
{ /* The rest of the code goes here*/

Related

Call to a member function getNbr() on array

i want to select all users in the database that have the role ROLE_USER only but i get this problm when i call the function they say "Call to a member function getNbr() on null" i think bcoz i use Findby() , bcoz i use the same function in another call and it works great look at the code :
public function indexAction(Request $request)
{
$us = $this->getDoctrine()->getManager();
$locationus = $us->getRepository('AppBundle:Usr')->findBy(
[ 'roles' => ["ROLE_USER"] ]);
echo $nb_us = $locationus->getNbr();
if($authChecker->isGranted(['ROLE_ADMIN']))
{
return $this->render('settingAdmin/profiladmin.html.twig' , array(
'nb_us' => $nb_us,
));
}
and this is the other function in the UserRepository:
class UserRepository extends \Doctrine\ORM\EntityRepository
{
public function getNbr() {
return $this->createQueryBuilder('l')
->select('COUNT(l)')
->getQuery()
->getSingleScalarResult();
}
}
getNbr is method of UserRepository class, so it can be called only for this UserRepository class instance. This method returns total users count.
findBy returns array of entities (in you case all users with role ROLE_USER), not UserRepository class instance, so you can't use getNbr in context of this variable
If you want to get the length of array of entities (in you case all users with role ROLE_USER), just use count function:
echo $nb_us = count($locationus);
if($authChecker->isGranted(['ROLE_ADMIN']))
{
return $this->render('settingAdmin/profiladmin.html.twig' , array(
'nb_us' => $nb_us, 'locationus' => $locationus
));
}
There looks to be quite many things going on in the code there:
1) $us->getRepository('AppBundle:Usr') is probably typoed and should be $us->getRepository('AppBundle:User') instead (?) In general it would be safer to use $us->getRepository(AppBundle\User::class) so that syntax errors can be caught easier/earlier.
2) You are trying to invoke repository method on array with $locationus->getNbr() which is incorrect on multiple accounts (you cannot invoke functions on arrays - and repository methods cannot be invoked from entities either).
3) why is the code using echo?
4) as an additional note (assuming that this is roughly the full intended code), it would make sense to move all the getters & handling inside the if section so that the code will perform better (it doesn't do unnecessary database queries etc when the user doesn't have enough rights to access the view/information).
If I understood the intention correctly, in this case, the second repository function getNbr is superfluous here. If that is intending to just calculate the number of instances returned by the first find:
$locationus = $us->getRepository('AppBundle:User')->findBy(['roles' => ["ROLE_USER"] ]);
$nb_us = count($locationus);
Or alternatively (if you want to use and fix the getNbr repository function) then you don't need the first repository getter. This will require some rewriting of the repository function as well though:
$nb_us = $us->getRepository('AppBundle:User')->getNbr("ROLE_USER");

Understanding problem Doctrine 2 ORM Query Builder in Symfony 4

I hope there are a few helpful and prefer German Symfony experts. For many years I have been working with PHP and now I tryed in a framework. I chose Symfony now because I like the components the most. The QueryBuilder and I stand on a war foot - I just do not understand it. Storing values works very well so far, though I doubt that I'm doing this in the sense of the framework. I'm really helpless. Currently I managing it by chasing everything in raw format but I'm not really happy with it.
How I can implement the following with Doctrine?
use App\Entity\Questions;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Doctrine\ORM\EntityManager;
class QuestionsController extends AbstractController
{
/**
* #Route("/addquestion", methods={"POST","HEAD"})
*/
public function addQuestion()
{
$entityManager = $this->getDoctrine()->getManager();
$RAW_QUERY = 'SELECT COUNT(*) AS c FROM questions WHERE questToID = '.$_POST['u'].' AND questFrom = '.$this->getUser()->getId().';';
$statement = $entityManager->getConnection()->prepare($RAW_QUERY);
$statement->execute();
$total = $statement->fetchAll();
if($total[0]['c'] >= 3)
{
return new Response('error|Du kannst der selben Person maximal drei Fragen stellen.');
}
[...]
I have already tried to implement this, and many other things (no avail):
Count Rows in Doctrine QueryBuilder
Since I speak bad English, I very much hope that you understand me…
so getting the value with $_POST['u'] is not recommended her you can either pass the value with the route an retrieve it as a parameter for the function:
/**
* #Route("/addquestion/{u}", methods={"POST","HEAD"})
*/
public function addQuestion($u)
or you can get it out from the request it self:
/**
* #Route("/addquestion", methods={"POST","HEAD"})
*/
public function addQuestion(Request $request)
if you want to use the query builder the best practice would be to create a repository for you entity and make you the query there:
namespace App\Repository;
use App\Entity\Questions;
use Doctrine\ORM\EntityRepository;
class QuestionsRepository extends EntityRepository
{
public function findUserQuestions($user, $u)
{
$queryBuilder = $this->createQueryBuilder('c')
->where('c.questToID = :questToID')
->andWhere('c.questFrom = :questFrom')
->setParameters(['questToID'=>$u,'questFrom'=>$user]);
return $queryBuilder->getQuery()->getResult();
}
and in you controller you can get the result:
$userQuestions = $this->getDoctrine()->getRepository(Questions::class)->findUserQuestions($this->getUser(), $u);
count($userQuestions);
i hope this will be useful in your case and explain you a bit how its done in symfony
One simple way of doing that would be something like this (with the assumption that you have a "Question" entity - and that the fields defined there are matching with the column names from your raw query).
$em = $this->getDoctrine()->getManager();
$userQuestions = $em->getRepository(Question:class)->findAll(['questToID' => $_POST['u'], 'questFrom ' => $this->getUser()->getId()]);
$total = count($userQuestion);
Or if you prefer to have just the count from the query instead of fetching all the matching objects and counting them, you can write the query builder part something like this (in this format this is meant to be written in your controller as you have with your raw query - in general the "correct" place would be QuestionRepository.php class from where this would be just called in controller):
$em = $this->getDoctrine()->getManager();
$qb = $em->createQueryBuilder('q');
$qb->select('count(q)');
$qb->andWhere('q.questToID = :questToID');
$qb->andWhere('q.questFrom = :questFrom');
$qb->setParameter('questToID', $_POST['u']);
$qb->setParameter('questFrom ', $this->getUser()->getId());
$total = $qb->getQuery()->getSingleScalarResult();

How to access Doctrine defined methods after custom Repository is made?

I am developing a site using ZF2 and Doctrine. The problem I am facing is I am using Doctrine predefined object methods like findAll(), findOneBy(), findBy() etc in my code. For some custom actions I have prepared a custom Repository for one of my entities. Now I can't access the predefined methods. I have already written code by using findAll() method. But after building a repository I can't simply access findAll() method. How can I both access my custom defined methods along with Doctrine defined methods?
For example:
I am using findOneBy() like this:
$udata = $this->em()->getRepository('Application\Entity\Usermain')->findOneBy(array('userEmail' => 'subh.laha#gmail.com'));
Now I have prepared UsermainRepository like below:
namespace Application\Entity\Repositories;
use Doctrine\ORM\EntityRepository;
use Doctrine\Common\Persistence\ObjectRepository;
class UsermainRepository extends EntityRepository
{
protected $sl;
public function __construct($sl){
$this->sl = $sl;
}
public function customFind($arr)
{
$qb = $this->sl->createQueryBuilder();
$whereStr = '';
if(count($arr)){
foreach($arr as $kvarr=>$varr){
$whereStr .= "u.$kvarr = '".$varr."'";
}
}
$qry = $qb->select('u')
->from('Application\Entity\Usermain','u')
->where($whereStr)
->getQuery()
->getResult();
return $qry;
}
}
Now I can access
$udata = $this->em()->getRepository('Application\Entity\Usermain')->customFind(array('userEmail' => 'subh.laha#gmail.com'));
But Not
$udata = $this->em()->getRepository('Application\Entity\Usermain')->findOneBy(array('userEmail' => 'subh.laha#gmail.com'));
Why? I have already written code by using doctrine defined methods. What can I do now?
I believe you are getting this error because you have overridden the repository's constructor method but aren't calling the parent constructor so required parameters aren't being properly set.
I think your code is not correct, You can use the below query instead of writing complex custom object.
$query = $this->getEntityManager()->createQueryBuilder()
->select('U.id,U.name')
->from('Application\Entity\StudentClass', 'U')
->where('U.pkStudentClass = :pkStudentClass')
->setParameter('pkStudentClass', 1)
->setMaxResults(20);
->orderBy('id', 'DESC')
->getQuery();
$result = $query->getScalarResult();
There are several problems exist in your approach;
I think trying to access the service locator's itself in an entity repository is bad idea. You shouldn't need service container in repository level.
The second detail is, when extending any class, you need to check out, read and respect the signature of the parent. In your case, you're overriding the parent's __construct. Calling parent::__construct() may seems like a solution but it's not. You'll soon realize that you also need a custom repository factory to pass additional arguments to constructor while keeping the current functionality. No way.
This is more important than others: you believe that $this->sl->createQueryBuilder() returns query builder instance. Theoretically seems like working but $this->sl is not service locator, service locator doesn't knows anything about query builders, it's just EntityManager instance which passed to your constructor.
Try this:
<?php
namespace Application\Entity\Repositories;
use Doctrine\ORM\EntityRepository;
class UsermainRepository extends EntityRepository
{
public function customFind($arr)
{
// Just pass an alias for your entity
$qb = $this->createQueryBuilder('u');
$whereStr = '';
if (count($arr)) {
foreach ($arr as $kvarr => $varr) {
$whereStr .= "u.$kvarr = '".$varr."'";
}
}
return $qb->where($whereStr)
->getQuery()
->getResult();
}
}
Finally, in your Application\Entity\Usermain entity, you'll also need telling about your custom repository to doctrine since you don't want to use default EntityRepository :
namespace Application\Entity;
/**
* #ORM\Entity(repositoryClass="Application\Entity\Repositories\UsermainRepository")
*/
class Usermain
{
}
Now in your controller (or service) level, you can test:
$em = $this->getServiceLocator()->get('doctrine.entitymanager.orm_default');
$repo = $em->getRepository('Application\Entity\Usermain');
// repo is a UsermainRepository instance
// this should work:
$udata = $repo->customFind(array('userEmail' => 'subh.laha#gmail.com'));
// this should also work
$udata = $repo->findOneBy(array('userEmail' => 'subh.laha#gmail.com'));
I strongly recommend carefully reading of Working With Objects section of the documentation before diving into deeps.

Laravel Custom Model Methods

Whenever I add additional logic to Eloquent models, I end up having to make it a static method (i.e. less than ideal) in order to call it from the model's facade. I've tried searching a lot on how to do this the proper way and pretty much all results talk about creating methods that return portions of a Query Builder interface. I'm trying to figure out how to add methods that can return anything and be called using the model's facade.
For example, lets say I have a model called Car and want to get them all:
$cars = Car::all();
Great, except for now, let's say I want to sort the result into a multidimensional array by make so my result may look like this:
$cars = array(
'Ford' => array(
'F-150' => '...',
'Escape' => '...',
),
'Honda' => array(
'Accord' => '...',
'Civic' => '...',
),
);
Taking that theoretical example, I am tempted to create a method that can be called like:
$cars = Car::getAllSortedByMake();
For a moment, lets forget the terrible method name and the fact that it is tightly coupled to the data structure. If I make a method like this in the model:
public function getAllSortedByMake()
{
// Process and return resulting array
return array('...');
}
And finally call it in my controller, I will get this Exception thrown:
Non-static method Car::getAllSortedByMake() should not be called statically, assuming $this from incompatible context
TL;DR: How can I add custom functionality that makes sense to be in the model without making it a static method and call it using the model's facade?
Edit:
This is a theoretical example. Perhaps a rephrase of the question would make more sense. Why are certain non-static methods such as all() or which() available on the facade of an Eloquent model, but not additional methods added into the model? This means that the __call magic method is being used, but how can I make it recognize my own functions in the model?
Probably a better example over the "sorting" is if I needed to run an calculation or algorithm on a piece of data:
$validSPG = Chemical::isValidSpecificGravity(-1.43);
To me, it makes sense for something like that to be in the model as it is domain specific.
My question is at more of a fundamental level such as why is all()
accessible via the facade?
If you look at the Laravel Core - all() is actually a static function
public static function all($columns = array('*'))
You have two options:
public static function getAllSortedByMake()
{
return Car::where('....')->get();
}
or
public function scopeGetAllSortedByMake($query)
{
return $query->where('...')->get();
}
Both will allow you to do
Car::getAllSortedByMake();
Actually you can extend Eloquent Builder and put custom methods there.
Steps to extend builder :
1.Create custom builder
<?php
namespace App;
class CustomBuilder extends \Illuminate\Database\Eloquent\Builder
{
public function test()
{
$this->where(['id' => 1]);
return $this;
}
}
2.Add this method to your base model :
public function newEloquentBuilder($query)
{
return new CustomBuilder($query);
}
3.Run query with methods inside your custom builder :
User::where('first_name', 'like', 'a')
->test()
->get();
for above code generated mysql query will be :
select * from `users` where `first_name` like ? and (`id` = ?) and `users`.`deleted_at` is null
PS:
First Laurence example is code more suitable for you repository not for model, but also you can't pipe more methods with this approach :
public static function getAllSortedByMake()
{
return Car::where('....')->get();
}
Second Laurence example is event worst.
public function scopeGetAllSortedByMake($query)
{
return $query->where('...')->get();
}
Many people suggest using scopes for extend laravel builder but that is actually bad solution because scopes are isolated by eloquent builder and you won't get the same query with same commands inside vs outside scope. I proposed PR for change whether scopes should be isolated but Taylor ignored me.
More explanation :
For example if you have scopes like this one :
public function scopeWhereTest($builder, $column, $operator = null, $value = null, $boolean = 'and')
{
$builder->where($column, $operator, $value, $boolean);
}
and two eloquent queries :
User::where(function($query){
$query->where('first_name', 'like', 'a');
$query->where('first_name', 'like', 'b');
})->get();
vs
User::where(function($query){
$query->where('first_name', 'like', 'a');
$query->whereTest('first_name', 'like', 'b');
})->get();
Generated queries would be :
select * from `users` where (`first_name` like ? and `first_name` like ?) and `users`.`deleted_at` is null
vs
select * from `users` where (`first_name` like ? and (`id` = ?)) and `users`.`deleted_at` is null
on first sight queries look the same but there are not. For this simple query maybe it does not matter but for complicated queries it does, so please don't use scopes for extending builder :)
for better dynamic code, rather than using Model class name "Car",
just use "static" or "self"
public static function getAllSortedByMake()
{
//to return "Illuminate\Database\Query\Builder" class object you can add another where as you want
return static::where('...');
//or return already as collection object
return static::where('...')->get();
}
Laravel model custom methods -> best way is using traits
Step #1: Create a trait
Step #2: Add the trait to model
Step #3: Use the method
User::first()->confirmEmailNow()
app/Model/User.php
use App\Traits\EmailConfirmation;
class User extends Authenticatable
{
use EmailConfirmation;
//...
}
app/Traits/EmailConfirmation.php
<?php
namespace App\Traits;
trait EmailConfirmation
{
/**
* Set email_verified_at to now and save.
*
*/
public function confirmEmailNow()
{
$this->email_verified_at = now();
$this->save();
return $this;
}
}

doctrine 2 and zend paginator

i want to use doctrine with zend_paginator
here some example query :
$allArticleObj =
$this->_em->getRepository('Articles');
$qb = $this->_em->createQueryBuilder();
$qb->add('select', 'a')
->add('from', 'Articles a')
->setFirstResult(0)
->setMaxResults(5);
is there any example code to show we can write a zend_paginator adapter for doctrine 2 query builder?
You don't need to implement Zend_Paginator_Adapter_Interface. It is already implement by Zend_Paginator_Adapter_Iterator.
You can simply pass Doctrines' Paginator to Zend_Paginator_Adapter_Iterator, which you pass to Zend_Paginator. Then you call Zend_Paginator::setItemCountPerPage($perPage) and Zend_Paginator::setCurrentPageNumber($current_page). Like this:
use Doctrine\ORM\Tools\Pagination as Paginator; // goes at top of file
SomeController::someAction()
{
$dql = "SELECT s, c FROM Square\Entity\StampItem s JOIN s.country c ".' ORDER BY '. $orderBy . ' ' . $dir;
$query = $this->getEntityManager()->createQuery($dql);
$d2_paginator = new Paginator($query); \\
$d2_paginator_iter = $d2_paginator->getIterator(); // returns \ArrayIterator object
$adapter = new \Zend_Paginator_Adapter_Iterator($d2_paginator_iter);
$zend_paginator = new \Zend_Paginator($adapter);
$zend_paginator->setItemCountPerPage($perPage)
->setCurrentPageNumber($current_page);
$this->view->paginator = $zend_paginator; //Then in your view, use it just like your currently use
}
Then you use paginator in the view script just like you ordinarly do.
Explanation:
Zend_Paginator's constructor can take a Zend_Paginator_Adapter_Interface, which Zend_Paginator_Adpater_Iterator implements. Now, Zend_Paginator_Adapter_Iterator's constructor takes an \Iterator interface. This \Iterator must also implement \Countable (as you can see by looking at Zend_Paginator_Adapter_Iterator's constructor). Since Paginator::getIterator() method returns an \ArrayIterator, it by definition it fits the bill (since \ArrayIterator implements both \Iterator and \Countable).
See this port from Doctrine 1 to Docrine 2 of the code for "Zend Framework: A Beginner's Guide" from Doctrine 1 to Doctrine: https://github.com/kkruecke/zf-beginners-doctrine2. It includes code for paginating with Zend_Paginator using Zend_Paginator_Adapter_Iterator with Doctrine 2' Doctrine\ORM\Tools\Pagination\Paginator.
Code is here (although it might not all work with latest DoctrineORM 2.2) but the example is valid: https://github.com/kkruecke/zf-beginners-doctrine2/tree/master/ch7
I was very surprised how hard it was to find an adapter example that doesn't cause performance issues with large collections for Doctrine 2 and ZF1.
My soultion does use the the Doctrine\ORM\Tools\Pagination\Paginator from Doctrine 2.2 just like #Kurt's answer; however the difference that this will return the doctrine paginator (Which is it's self an \Iterator) from getItems without the entire query results being hydrated.
use Doctrine\ORM\Tools\Pagination\Paginator as DoctrinePaginator;
use Doctrine\ORM\Query;
class DoctrinePaginatorAdapter implements \Zend_Paginator_Adapter_Interface
{
protected $query;
public function __construct(Query $query)
{
$this->query = $query;
}
public function count()
{
return $this->createDoctrinePaginator($this->query)->count();
}
public function getItems($offset, $itemCountPerPage)
{
$this->query->setFirstResult($offset)
->setMaxResults($itemCountPerPage);
return $this->createDoctrinePaginator($this->query);
}
protected function createDoctrinePaginator(Query $query, $isFetchJoinQuery = true)
{
return new DoctrinePaginator($query, $isFetchJoinQuery);
}
}
The upshot, of course, is that you have to implement the Zend_Paginator_Adapter_Interface, which essentially means implementing the two methods:
count()
getItems($offset, $perPage)
Your adapter would accept the Doctrine query as a constructor argument.
In principle, the getItems() part is actually straightforward. Simply, add the $offset and $perPage restrictions to the query - as you are doing in your sample - and execute the query.
In practice, it's the count() business that tends to be tricky. I'd follow the example of Zend_Paginator_Adapter_DbSelect, replacing the Zend_Db operations with their Doctrine analogues.
Please change
use Doctrine\ORM\Tools\Pagination as Paginator;
to
use Doctrine\ORM\Tools\Pagination\Paginator as Paginator;

Categories