I have a problem with getting data from the database using the expr() method function. I would like to get data where isPublic = true and objectType = $objectType OR user = $user and objectType = $objectType, no matter what the value of isPublic is.
I'm getting this error:
[Semantical Error] line 0, col 76 near 'user-avatar)': Error: 'user' is not defined.
My code in repository:
public function findByObjectType($objectType, $user)
{
$qb = $this->createQueryBuilder('s');
return $qb->where($qb->expr()->andX(
$qb->expr()->eq('s.isPublic', true),
$qb->expr()->eq('s.objectType', $objectType)
))
->orWhere($qb->expr()->andX(
$qb->expr()->eq('s.user', $user->getId()),
$qb->expr()->eq('s.objectType', $objectType)
))
->getQuery()
->getResult();
}
where: $objectType = 'user-avatar'; $user = UserInterface
expr()->eq() will treat the expression as literals, trying to use them literally as they appear on method call.
As mentioned by the library author:
You are not using parameter binding. Expressions use string concatenation internally, so this outcome is actually expected.
In your case, you should be doing something like::
return $qb->where($qb->expr()->andX(
$qb->expr()->eq('s.isPublic', ':true'),
$qb->expr()->eq('s.objectType', ':objectType')
))
->orWhere($qb->expr()->andX(
$qb->expr()->eq('s.user', ':userId'),
$qb->expr()->eq('s.objectType', ':objectType')
))
->setParameter('true', true)
->setParameter('userId', $user->getId())
->setParameter('objectType', $objectType)
->getQuery()
->getResult();
This way your code is easier to read, safer and more portable.
When using $qb->expr()->eq() you will need supply the exact value for the query. In this case you need to change your query to something like this:
$qb->expr()->eq('s.objectType', '"' . $objectType .'"')
This way to string will be correctly quoted in the db query. The same goes for booleans by the way. Casting true to a string will result in 1. That's why you didn't encounter an error in this case. false however gets cast to an empty string, which would result in an error.
To better understand what's going on, here is the part of code that converts the eq() expression to the query:
/**
* #return string
*/
public function __toString()
{
return $this->leftExpr . ' ' . $this->operator . ' ' . $this->rightExpr;
}
Related
I'm trying to do this SQL query with Doctrine QueryBuilder:
SELECT * FROM events WHERE NOT id in (SELECT event_id FROM ues WHERE user_id = $userID)
The UserEventStatus has foreign keys from User and event, as well as an integer for status.
I now want to query all events that dont have an entry in UserEventStatus from an particular User.
My function for this in the EventRepository looks like this:
public function getUnReactedEvents(int $userID){
$expr = $this->getEntityManager()->getExpressionBuilder();
$originalQuery = $this->createQueryBuilder('e');
$subquery= $this->createQueryBuilder('b');
$originalQuery->where(
$expr->not(
$expr->in(
'e.id',
$subquery
->select('ues.user')
->from('App/Entity/UserEventStatus', "ues")
->where(
$expr->eq('ues.user', $userID)
)
)
)
);
return $originalQuery->getQuery()->getResult();
}
But i get an error that says:
Error: Method Doctrine\Common\Collections\ArrayCollection::__toString() must not throw an exception, caught ErrorException: Catchable Fatal Error: Object of class Doctrine\ORM\EntityManager could not be converted to string (500 Internal Server Error)
Can anyone help me or point me to right point in the docs? Cause i failed to find something that describes my problem.
And another thing is, that I don't know if its possible, but it would be nice. Can I somehow make direct Object requests? I mean not with the string App/Entity/UserEventStatus but with something like UserEventStatus::class or something.
Thanks for your help in advance. :)
EDIT: It has to be $originalQuery->getQuery()->getResult() of course.
If its like it was with $subquery instead i recive [Semantical Error] line I0, col 41 near 'App/Entity/UserEventStatus': Error: Class 'App' is not defined. (500 Internal Server Error)
Second EDIT:
$expr = $this->getEntityManager()->getExpressionBuilder();
$queryBuilder = $this->createQueryBuilder('e');
$subquery= $this->createQueryBuilder('b')
->select('ues.user')
->from('UserEventStatus', "ues")
->add('where', $expr->eq('ues.user', $userID));
$originalQueryExpression = $expr->not($expr->in('e.id', $subquery));
$queryBuilder->add('where', $originalQueryExpression);
return $queryBuilder->getQuery()->getResult();
Third EDIT: Thanks to #Dilek I made it work with a JOIN. This is the final Query:
$queryBuilder = $this->createQueryBuilder('e')
->leftJoin('App\Entity\UserEventStatus', 'ues', 'WITH', 'ues.user=:userID')
->setParameter('userID', $userID)
->where($expr->orX($expr->not(
$expr->eq('e.id','ues.event')
),
$expr->not($expr->eq('ues.user', $userID)))
);
return $queryBuilder->getQuery()->getResult();
Building AND WHERE into a Query
public function search($term)
{
return $this->createQueryBuilder('cat')
->andWhere('cat.name = :searchTerm')
->setParameter('searchTerm', $term)
->getQuery()
->execute();
}
simple is: ->where('cat.name = :searchTerm')
UPDATE :
I think you need to use where in
$qb->add('where', $qb->expr()->in('ues.user', $userID));
And WHERE Or WHERE
I am very new to Laravel and am going through the tutorials and am stuck on something.
I have a complex query that I need to reuse with one parameter change in the where clause. I added this as a query scope in my Model and then call it from my corresponding Controller. When I try to return the data though I am getting this error:
Object of class Illuminate\Database\Eloquent\Builder could not be converted to string
Here is the query scope:
public function scopeCrosstab($wellID)
{
return static::select('sampleDate', \DB::raw("
max(if(chemID=1, pfcLevel, ' ')) as 'PFOA', max(if(chemID=1, noteAbr, ' ')) as 'PFOANote'
"))
->leftJoin('SampleNote', 'WellSample.noteID', '=', 'SampleNote.noteID')
->where('wellID', '=', $wellID)
->groupBy('sampleDate');
}
Here is the Controller code:
public function smith()
{
$wellSamples = WellSample::crosstab(2);
return $wellSamples->get();
//return view('pages.wellsample', compact('wellSamples'));
}
I have tried many different permutations of the code with quotes, with double quotes etc. If I hard code the value in the query scope it works, but I need to be able to make it dynamic.
Scope methods take at least one parameter, the first of which must be $query. You then build off of the query variable that is passed to your scope method. Like this:
public function scopeCrosstab($query, $wellID)
{
return $query->select('sampleDate', \DB::raw("
max(if(chemID=1, pfcLevel, ' ')) as 'PFOA', max(if(chemID=1, noteAbr, ' ')) as 'PFOANote'
"))
->leftJoin('SampleNote', 'WellSample.noteID', '=', 'SampleNote.noteID')
->where('wellID', '=', $wellID)
->groupBy('sampleDate');
}
I am trying to make a search query for my website blogs using yii2 QueryBuilder , but there is an error like this when i try to execute my query with ->all() . here is the error : strtr() expects parameter 1 to be string, object given . And here is my model and controller . I have no idea what is causing the problem .
controller :
public function actionSearchBlog()
{
$model = new Blog();
if ($model->load(Yii::$app->request->post())) {
Blog::searchBlog($model->search);
} else {
return $this->render('search',['model' => $model]);
}
}
Model :
public static function searchBlog($search = null)
{
$search = new Query();
$result = $search->select('id','title','blog','picture')
->from('blog')
->where(['like' , 'title' , $search])
->orWhere(['like' , 'blog' , $search])
->all();
echo '<pre>';
var_dump($result);
die();
}
I tried the query without ->all() at the end , but the var_dump value will be the the query itself and it won't be executed . and with ->all() I get that error.
public static function searchBlog($search = null)
{
$query = new Query();
$result = $query->select('id','title','blog','picture')
->from('blog')
->where(['like' , 'title' , $search])
->orWhere(['like' , 'blog' , $search])
->all();
echo '<pre>';
var_dump($result);
die();
}
This will work. But start using IDE, and watch on variables you are using.
Try your ActiveQuery as:--
$result = (new Query())->select('id','title','blog','picture')
->from('blog')
->where(['like' , 'title' , $search])
->orWhere(['like' , 'blog' , $search])
->all();
echo '<pre>';
var_dump($result);
die();
Are you sure the $search is string or array?
like: operand 1 should be a column or DB expression, and operand 2 be
a string or an array representing the values that the column or DB
expression should be like. For example, ['like', 'name', 'tester']
will generate name LIKE '%tester%'.
When the value range is given as an array, multiple LIKE predicates
will be generated and concatenated using AND. For example, ['like',
'name', ['test', 'sample']] will generate name LIKE '%test%' AND
name LIKE '%sample%'. The method will properly quote the column name
and escape special characters in the values.
Sometimes, you may want to add the percentage characters to the
matching value by yourself, you may supply a third operand false to
do so. For example, ['like', 'name', '%tester', false] will generate
name LIKE '%tester'.
I'm building a function for filter some records based on four parameters: $codigo, $anno, $term and $comite_tecnico. This is what I build until now:
public function filtrarNorma($codigo = null, $anno = null, $term = null, $comite_tecnico = null)
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb
->select('n')
->from("AppBundle:Norma", "n");
if ($codigo != NULL) {
$qb->where($qb->expr()->like('n.numero', ':codigo'));
$qb->setParameter('codigo', '%' . $codigo . '%');
}
if ($anno != NULL) {
$qb->orWhere($qb->expr()->like('n.anno', ':anno'));
$qb->setParameter('anno', '%' . $anno . '%');
}
if ($term != NULL) {
$qb->orWhere($qb->expr()->like('n.nombre', ':term'));
$qb->setParameter('term', '%' . $term. '%');
}
if ($comite_tecnico != NULL) {
$qb->orWhere($qb->expr()->like('n.comite_tecnico', ':comite_tecnico'));
$qb->setParameter('comite_tecnico', '%' . $comite_tecnico . '%');
}
return $qb->getQuery()->getResult();
}
Any time I try to perform a query I get this error:
An exception occurred while executing 'SELECT n0_.numero AS numero0,
n0_.anno AS anno1, n0_.id AS id2, n0_.nombre AS nombre3, n0_.activo AS
activo4, n0_.comite_tecnico_id AS comite_tecnico_id5 FROM
nomencladores.norma n0_ WHERE n0_.numero LIKE ? OR n0_.anno LIKE ?'
with params ["34", 45]:
SQLSTATE[42883]: Undefined function: 7 ERROR: operator does not exist:
integer ~~ unknown LINE 1: ...dores.norma n0_ WHERE n0_.numero LIKE $1
OR n0_.anno LIKE $2 ^ HINT: No operator matches the given name and
argument type(s). You might need to add explicit type casts.
That's telling me that I need to cast some of those parameters before send it to the PgSQL DB and execute the query to get results but my question is, how I do that on Doctrine2 DQL? It's possible? Any workaround or trick or something else? I've found this documentation but don't know which function apply and also how, can any give me some help or advice around this?
Edit with new tests
After users suggestions I made some changes to my code and now it looks like:
public function filtrarNorma($codigo = null, $anno = null, $term = null, $comite_tecnico = null)
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb
->select('n')
->from("AppBundle:Norma", "n");
if ($codigo != NULL) {
$qb->where($qb->expr()->like('n.numero', ':codigo'));
$qb->setParameter('codigo', '%'.$codigo.'%', PDO::PARAM_STR);
}
if ($anno != NULL) {
$qb->orWhere($qb->expr()->like('n.anno', ':anno'));
$qb->setParameter('anno', $anno, PDO::PARAM_INT);
}
if ($term != NULL) {
$qb->orWhere($qb->expr()->like('n.nombre', ':term'));
$qb->setParameter('term', '%'.$term.'%', PDO::PARAM_STR);
}
if ($comite_tecnico != NULL) {
$qb->orWhere($qb->expr()->like('IDENTITY(n.comite_tecnico)', ':comite_tecnico'));
$qb->setParameter('comite_tecnico', '%'.$comite_tecnico.'%', PDO::PARAM_INT);
}
return $qb->getQuery()->getResult();
}
But once again, get the same error:
An exception occurred while executing 'SELECT n0_.numero AS numero0,
n0_.anno AS anno1, n0_.id AS id2, n0_.nombre AS nombre3, n0_.activo AS
activo4, n0_.comite_tecnico_id AS comite_tecnico_id5 FROM
nomencladores.norma n0_ WHERE n0_.numero LIKE ? OR n0_.anno LIKE ?'
with params ["%4%", "4"]:
SQLSTATE[42883]: Undefined function: 7 ERROR: operator does not exist:
integer ~~ unknown LINE 1: ...dores.norma n0_ WHERE n0_.numero LIKE $1
OR n0_.anno LIKE $2 ^ HINT: No operator matches the given name and
argument type(s). You might need to add explicit type casts.
And as you may notice in this case params are passed as should be: ["%4%", "4"] but why the error? Still not getting where it's
Another test
So, getting ride of Doctrine Query Builder and applying some Doctrine Query Language I moved the query from the code above to this one:
$em = $this->getEntityManager();
$query = $em->createQuery("SELECT n from AppBundle:Norma n WHERE n.numero LIKE '%:codigo%' OR n.anno LIKE '%:anno%' OR n.nombre LIKE '%:term%' OR IDENTITY(n.comite_tecnico) LIKE '%:comite_tecnico%'");
$query->setParameters(array(
'codigo' => $codigo,
'anno' => $anno,
'term' => $term,
'comite_tecnico' => $comite_tecnico
));
return $query->getResult();
But in this case I get this message:
Invalid parameter number: number of bound variables does not match
number of tokens
If the query is made by OR should be the four parameters required?
Your first try actually works for me all the time. You can convert your integers using strval()'.
'%' . strval($anno) . '%';
After a deep research I've found the solution to my problem and want to share with others too. I should said also thanks to #ErwinBrandstetter, #b.b3rn4rd for their time and support and to #Pradeep which finally give me the idea for research and finally get problem fixed and I did by enabling implicit casting support in PostgreSQL.
For enable implicit casts you must therefore execute the following commands in your PostgreSQL console when connected to the template1 database, so that any database created afterward will come with the required CASTs (if your database is already created, execute the commands in your database as well):
CREATE FUNCTION pg_catalog.text(integer) RETURNS text STRICT IMMUTABLE LANGUAGE SQL AS 'SELECT textin(int4out($1));';
CREATE CAST (integer AS text) WITH FUNCTION pg_catalog.text(integer) AS IMPLICIT;
COMMENT ON FUNCTION pg_catalog.text(integer) IS 'convert integer to text';
CREATE FUNCTION pg_catalog.text(bigint) RETURNS text STRICT IMMUTABLE LANGUAGE SQL AS 'SELECT textin(int8out($1));';
CREATE CAST (bigint AS text) WITH FUNCTION pg_catalog.text(bigint) AS IMPLICIT;
COMMENT ON FUNCTION pg_catalog.text(bigint) IS 'convert bigint to text';
That's all, after running that on the current DB I'm using and also on template1 for future ones and keeping conditions on my code as follow, all works fine and without any errors:
if ($codigo != null) {
$qb->where($qb->expr()->like('n.numero', ':codigo'));
$qb->setParameter('codigo', '%'.$codigo.'%', PDO::PARAM_STR);
}
if ($anno != null) {
$qb->orWhere($qb->expr()->like('n.anno', ':anno'));
$qb->setParameter('anno', '%'.$anno.'%', PDO::PARAM_STR);
}
if ($term != null) {
$qb->orWhere($qb->expr()->like('n.nombre', ':term'));
$qb->setParameter('term', '%'.$term.'%', PDO::PARAM_STR);
}
if ($comite_tecnico != null) {
$qb->orWhere($qb->expr()->like('IDENTITY(n.comite_tecnico)', ':comite_tecnico'));
$qb->setParameter('comite_tecnico', '%'.$comite_tecnico.'%', PDO::PARAM_STR);
}
Happy coding!!
I think in your last try the raw SQL string should look like this:
$query = $em->createQuery("SELECT n.*
FROM nomencladores.norma n
WHERE n.numero LIKE '%' || :codigo || '%' OR
cast(n.anno AS text) LIKE '%' || :anno || '%' OR
n.nombre LIKE '%' || :term || '%' OR
IDENTITY(n.comite_tecnico) LIKE '%' || :comite_tecnico || '%'");
Any other column here not text or varchar? Cast it, too.
Don't know the IDENTITY() function. A spillover from Doctrine, as well?
Still, I don't know much about Doctrine.
You're trying to use LIKE on an integer, which doesn't make sense.
Cast the integer to its text representation. This might work:
$qb->where($qb->expr()->like('CAST(n.numero AS text)', ':codigo'));
How can you do method chaining based on condition in laravel 4 ? Say if one value is not false then the method inside will be chained to the method called before the if statement.
Is it possible in laravel?
$data = User::where('username', $somevariable );
if(isset( $somevar_again ))
{
$data->where('age', 21);
}
$data->orderBy('reg_date', 'DESC')->get();
return $data->first();
// tried code above and its giving me wrong result
in codeigniter I can do this
$this->db->select('e.*, v.name_en as v_name_en')
->from($this->table_name . ' e, ' . $this->ptc_venues . ' v');
$this->db->where('e.venue_id_en = v.id');
if(isset($search)){
$this->db->where('(v.name_en LIKE "%'.$search.'%")');
}
$this->db->limit($limit, $start);
$this->db->order_by('e.added_date_en', 'DESC');
I believe your problem happened because you didn't store back the resulting query after each query builder method call.
$query = User::query();
// Checking for username if exists
if (!empty($username)) {
$query = $query->where('username', $username);
}
// Check for age if exists
if (isset($age)) {
$query = $query->where('age', $age);
}
// Ordering
$query = $query->orderBy('reg_date', 'DESC');
// Get the first result
// After this call, it is now an Eloquent model
$user = $query->first();
var_dump($user);
From Laravel 5.2 and onward, you can utilise Conditional Clauses/Statements:
Sometimes you may want statements to apply to a query only when
something else is true. For instance you may only want to apply a
where statement if a given input value is present on the incoming
request. You may accomplish this using the when method
The when method only executes the given Closure when the first parameter is true. If the first parameter is false, the Closure will not be executed.
You can use the code as follows:
$data = User::where('username', $somevariable)
->when( isset($somevar_again), function ($query) {
return $query->where('age', 21);
})
->orderBy('reg_date', 'DESC')
->get();
return $data->first();
Also, note that Laravel 5.3+, it has further been extended as documented below:
You may pass another Closure as the third parameter to the when
method. This Closure will execute if the first parameter evaluates as
false