Grouping WHERE clauses in Codeigniter - php

I want to produce the following SQL code using Active Records in Codeigniter:
WHERE name != 'Joe' AND (age < 69 OR id > 50)
Doing the following seems to be as far as I can get, I cant figure out how to group them
$this->db->select()->from('users')->where('name !=', 'Joe')->where('age <', 69)->or_where('id <', $id);
Any ideas? My SQL query is too complex so I dont wish to rewrite everything in traditional SQL.
UPDATE
My SQL code is dynamically generated depending on the values of certain parameters passed into the model method. The problem with not being able to use parenthesis causes a problem because the operator precedence is such that AND is evaluated first before OR.
*Here is a chunk of my active records code, where there are some other code before and after it:
... some $this->db->where() ...
... some $this->db->where() ...
if($price_range) {
$price_array = explode('.', $price_range);
for($i = 0; $i < count($price_array); $i++) {
if($i == 0) {
$this->db->where('places.price_range', $price_array[$i]);
} else {
$this->db->or_where('places.price_range', $price_array[$i]);
}
}
}
... some $this->db->where() ...
... some $this->db->where() ...
The problem comes because I am using $this->db->or_where() which introduces a OR clause that throws the operator precedence into disarray without being able to use ( ) to change the order.
** Is there any way to solve this? **

In Codeigniter 3.0.3 you can do it simple like this :
$this->db->select()
->from('users')
->where('name !=', 'Joe')
->group_start() // Open bracket
->where('age <', 69)
->or_where('id <', $id)
->group_end(); // Close bracket
Perhaps it can help

You can use one large string.
$this->db->select()->from('users')->where("name != 'Joe' AND (age < 69 OR id > 50)
");

The grouping of where clauses is not in CI by default. You have to extend the core and add in the ability. I have done so by doing something as follows:
class MY_DB_mysql_driver extends CI_DB_mysql_driver
{
public function __construct($params)
{
parent::__construct($params);
}
/**
* This function will allow you to do complex group where clauses in to c and (a AND b) or ( d and e)
* This function is needed as else the where clause will append an automatic AND in front of each where Thus if you wanted to do something
* like a AND ((b AND c) OR (d AND e)) you won't be able to as the where would insert it as a AND (AND (b...)) which is incorrect.
* Usage: start_group_where(key,value)->where(key,value)->close_group_where() or complex queries like
* open_bracket()->start_group_where(key,value)->where(key,value)->close_group_where()
* ->start_group_where(key,value,'','OR')->close_group_where()->close_bracket() would produce AND ((a AND b) OR (d))
* #param $key mixed the table columns prefix.columnname
* #param $value mixed the value of the key
* #param $escape string any escape as per CI
* #param $type the TYPE of query. By default it is set to 'AND'
* #return db object.
*/
function start_group_where($key,$value=NULL,$escape,$type="AND")
{
$this->open_bracket($type);
return parent::_where($key, $value,'',$escape);
}
/**
* Strictly used to have a consistent close function as the start_group_where. This essentially callse the close_bracket() function.
*/
function close_group_where()
{
return $this->close_bracket();
}
/**
* Allows to place a simple ( in a query and prepend it with the $type if needed.
* #param $type string add a ( to a query and prepend it with type. Default is $type.
* #param $return db object.
*/
function open_bracket($type="AND")
{
$this->ar_where[] = $type . " (";
return $this;
}
/**
* Allows to place a simple ) to a query.
*/
function close_bracket()
{
$this->ar_where[] = ")";
return $this;
}
}
Usage:
group_where_start(key,value)->where(key,value)->group_where_close()
or
complex queries like
open_bracket()->start_group_where(key,value)->where(key,value)->close_group_where()->start_group_where(key,value,'','OR')->close_group_where()->close_bracket() would produce AND ((a AND b) OR (d))

CI3 has all you need!
$this->db->select('*')->from('my_table')
->group_start()
->where('a', 'a')
->or_group_start()
->where('b', 'b')
->where('c', 'c')
->group_end()
->group_end()
->where('d', 'd')
->get();
https://www.codeigniter.com/userguide3/database/query_builder.html#query-grouping

What I've done is duplicate the and clause after the where, which is effectively the same as the long string selection.
$this->db->select()
->from('users')
->where('name !=', 'Joe')
->where('age <', 69)
->or_where('id <', $id)
->where('name !=', 'Joe');
The one large string way is probably better.

Solved. Dynamically generate the SQL query and plug it into $this->db->where(). Thanks guys!

Related

Call to undefined method CI_DB_mysqli_driver::start_group_where() [duplicate]

I want to produce the following SQL code using Active Records in Codeigniter:
WHERE name != 'Joe' AND (age < 69 OR id > 50)
Doing the following seems to be as far as I can get, I cant figure out how to group them
$this->db->select()->from('users')->where('name !=', 'Joe')->where('age <', 69)->or_where('id <', $id);
Any ideas? My SQL query is too complex so I dont wish to rewrite everything in traditional SQL.
UPDATE
My SQL code is dynamically generated depending on the values of certain parameters passed into the model method. The problem with not being able to use parenthesis causes a problem because the operator precedence is such that AND is evaluated first before OR.
*Here is a chunk of my active records code, where there are some other code before and after it:
... some $this->db->where() ...
... some $this->db->where() ...
if($price_range) {
$price_array = explode('.', $price_range);
for($i = 0; $i < count($price_array); $i++) {
if($i == 0) {
$this->db->where('places.price_range', $price_array[$i]);
} else {
$this->db->or_where('places.price_range', $price_array[$i]);
}
}
}
... some $this->db->where() ...
... some $this->db->where() ...
The problem comes because I am using $this->db->or_where() which introduces a OR clause that throws the operator precedence into disarray without being able to use ( ) to change the order.
** Is there any way to solve this? **
In Codeigniter 3.0.3 you can do it simple like this :
$this->db->select()
->from('users')
->where('name !=', 'Joe')
->group_start() // Open bracket
->where('age <', 69)
->or_where('id <', $id)
->group_end(); // Close bracket
Perhaps it can help
You can use one large string.
$this->db->select()->from('users')->where("name != 'Joe' AND (age < 69 OR id > 50)
");
The grouping of where clauses is not in CI by default. You have to extend the core and add in the ability. I have done so by doing something as follows:
class MY_DB_mysql_driver extends CI_DB_mysql_driver
{
public function __construct($params)
{
parent::__construct($params);
}
/**
* This function will allow you to do complex group where clauses in to c and (a AND b) or ( d and e)
* This function is needed as else the where clause will append an automatic AND in front of each where Thus if you wanted to do something
* like a AND ((b AND c) OR (d AND e)) you won't be able to as the where would insert it as a AND (AND (b...)) which is incorrect.
* Usage: start_group_where(key,value)->where(key,value)->close_group_where() or complex queries like
* open_bracket()->start_group_where(key,value)->where(key,value)->close_group_where()
* ->start_group_where(key,value,'','OR')->close_group_where()->close_bracket() would produce AND ((a AND b) OR (d))
* #param $key mixed the table columns prefix.columnname
* #param $value mixed the value of the key
* #param $escape string any escape as per CI
* #param $type the TYPE of query. By default it is set to 'AND'
* #return db object.
*/
function start_group_where($key,$value=NULL,$escape,$type="AND")
{
$this->open_bracket($type);
return parent::_where($key, $value,'',$escape);
}
/**
* Strictly used to have a consistent close function as the start_group_where. This essentially callse the close_bracket() function.
*/
function close_group_where()
{
return $this->close_bracket();
}
/**
* Allows to place a simple ( in a query and prepend it with the $type if needed.
* #param $type string add a ( to a query and prepend it with type. Default is $type.
* #param $return db object.
*/
function open_bracket($type="AND")
{
$this->ar_where[] = $type . " (";
return $this;
}
/**
* Allows to place a simple ) to a query.
*/
function close_bracket()
{
$this->ar_where[] = ")";
return $this;
}
}
Usage:
group_where_start(key,value)->where(key,value)->group_where_close()
or
complex queries like
open_bracket()->start_group_where(key,value)->where(key,value)->close_group_where()->start_group_where(key,value,'','OR')->close_group_where()->close_bracket() would produce AND ((a AND b) OR (d))
CI3 has all you need!
$this->db->select('*')->from('my_table')
->group_start()
->where('a', 'a')
->or_group_start()
->where('b', 'b')
->where('c', 'c')
->group_end()
->group_end()
->where('d', 'd')
->get();
https://www.codeigniter.com/userguide3/database/query_builder.html#query-grouping
What I've done is duplicate the and clause after the where, which is effectively the same as the long string selection.
$this->db->select()
->from('users')
->where('name !=', 'Joe')
->where('age <', 69)
->or_where('id <', $id)
->where('name !=', 'Joe');
The one large string way is probably better.
Solved. Dynamically generate the SQL query and plug it into $this->db->where(). Thanks guys!

Symfony 2 - fetch the last inserted row from table

How can I rewrite this code in order to get last inserted record from the table?
$repository = $entityManager->getRepository('AdminBundle:MyTable');
$product = $repository->find($id);
I tried something like
$repository->findBy(array('id','DESC')->setMaxResults(1);
But it did not work for me.
You could get the latest record by using findBy() with order by, limit and offset parameters
$results = $repository->findBy(array(),array('id'=>'DESC'),1,0);
First argument is for filter criteria
Second argument takes order by criteria
Third argument is for limit
Fourth argument sets offset
Note it will return you the results set as array of objects so you can get single object from result as $results[0]
FindBy() Examples
Instead of hacking code where you want to use it, you can also create a repository method and call it when necessary.
/**
* Repository method for finding the newest inserted
* entry inside the database. Will return the latest
* entry when one is existent, otherwise will return
* null.
*
* #return MyTable|null
*/
public function findLastInserted()
{
return $this
->createQueryBuilder("e")
->orderBy("id", "DESC")
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
}
References:
https://symfony.com/doc/current/doctrine.html#querying-for-objects-the-repository
After looking for one I decided to try it myself, I think it was much less verbose:
$myRepository->findOneBy([], ['id' => 'DESC']);
Please try the below one
$repository = $entityManager->getRepository('AdminBundle:MyTable');
$repository->setMaxResults(1)->orderBy('id', 'DESC');
$results = $repository->getQuery()->getSingleResult();
Reference:
https://undebugable.wordpress.com/2016/01/27/symfony2-querybuilder-find-first-and-find-last-record-in-table/
You can add these functions to your repository:
public function getLastRow(): ?YourEntity
{
return $this->findOneBy([], ['id' => 'DESC']);
}
public function getLastId(): int
{
$lastRow = $this->getLastRow();
return $lastRow ? $lastRow->getId() : 0;
}
You can be collected by getting the id of the inserted object
$em->persist($entity);
$em->flush();
$entity->getId();
OR
$entitymanager->getRepository("entity")->findBy([],["id"=>desc])->getId();

Get all entities ordered with Doctrine query builder

I'm getting a little crazy with this. I have a PhoneCodes entity and I simply want to retrieve all entities ordered by a field so no where condition but I tried to achieve this by many ways and not working. Currently I have this:
$phonecodes = $this->getDoctrine()
->getRepository('AcmeDemoBundle:PhoneCodes')
->createQueryBuilder('p')
->orderBy('p.length', 'ASC')
->getQuery()
->getResult();
What's the way to do this? Thanks.
Your code should be something like this:
$phonecodes = $this->getEntityManager()
->createQueryBuilder()
->select("p")
->from("AcmeDemoBundle:PhoneCodes", "p")
->orderBy("p.length", "ASC")
->getQuery()
->getResult()
If you're in a controller just do this:
$phonecodes = $em->getRepository('AcmeDemoBundle:PhoneCodes')->findBy(
array(),//conditions, none in this case
array(//orderBy, multiple possible
"length"=>"asc"
)
);
This way you don't need to write a custom repository function.
If you wan't to create it as a repository function (e.g. in PhoneCodesRepository.php) do it that way:
/**
* Returns all phoneCodes hydrated to objects ordered by length
* #param string $order - ASC | DESC
* #return \Doctrine\Common\Collections\Collection
*/
function findAllOrderedByLength($order="ASC")
{
$qb = $this->createQueryBuilder("p");
$qb->orderBy("p.length", $order);
return $qb->getQuery()->getResult();
}
http://symfony.com/doc/current/book/doctrine.html#custom-repository-classes

use "()" in Fuelphp query builder

I'd like to use "()" in fuelphp's sql like this.
select * from shop where (item1=$item1 or item2=$item1) and flag=on;
I tried to express it like this;
$shop_query = DB::select()->from('shop');
$shop_query->where(function($shop_query){
$shop_query->where('item1','=',$item1)
->or_where('item2','=',$item1);
$shop_query ->and_where('flag','=','on');
However, This shows error: undefined index item1.$item1, and surely it has values.
How could I solve this?
You can use the grouping ->where_open/close method of the query builder:
public static function whatever($item1, ... the rest of your args)
{
$shop_query = DB::select()
->from('shop')
->where('flag', 'on')
->and_where_open()
->where('item1', $item1)
->or_where('item2', $item1)
->and_where_close()
->execute()->as_array(); // just change this to whatever you need
return $shop_query;
}
This turns into:
SELECT * FROM `shop` WHERE `flag` = 'on' AND (`item1` = '$item1' OR `item2` = '$item1')

How can I query raw via Eloquent?

I am trying to do a query in my Laravel app and I want to use a normal structure for my query. This class either does use Eloquent so I need to find something to do a query totally raw.
Might be something like Model::query($query);. Only that doesn't work.
You may try this:
// query can't be select * from table where
Model::select(DB::raw('query'))->get();
An Example:
Model::select(DB::raw('query'))
->whereNull('deleted_at')
->orderBy('id')
->get();
Also, you may use something like this (Using Query Builder):
$users = DB::table('users')
->select(DB::raw('count(*) as user_count, status'))
->where('status', '<>', 1)
->groupBy('status')
->get();
Also, you may try something like this (Using Query Builder):
$users = DB::select('select * from users where id = ?', array(1));
$users = DB::select( DB::raw("select * from users where username = :username"), array('username' => Input::get("username")));
Check more about Raw-Expressions on Laravel website.
You can use hydrate() function to convert your array to the Eloquent models, which Laravel itself internally uses to convert the query results to the models. It's not mentioned in the docs as far as I know.
Below code is equviolent to $userModels = User::where('id', '>', $userId)->get();:
$userData = DB::select('SELECT * FROM users WHERE id > ?', [$userId]);
$userModels = User::hydrate($userData);
hydrate() function is defined in \Illuminate\Database\Eloquent\Builder as:
/**
* Create a collection of models from plain arrays.
*
* #param array $items
* #return \Illuminate\Database\Eloquent\Collection
*/
public function hydrate(array $items) {}
use DB::statement('your raw query here'). Hope this helps.
I don't think you can by default. I've extended Eloquent and added the following method.
/**
* Creates models from the raw results (it does not check the fillable attributes and so on)
* #param array $rawResult
* #return Collection
*/
public static function modelsFromRawResults($rawResult = [])
{
$objects = [];
foreach($rawResult as $result)
{
$object = new static();
$object->setRawAttributes((array)$result, true);
$objects[] = $object;
}
return new Collection($objects);
}
You can then do something like this:
class User extends Elegant { // Elegant is my extension of Eloquent
public static function getWithSuperFancyQuery()
{
$result = DB::raw('super fancy query here, make sure you have the correct columns');
return static::modelsFromRawResults($result);
}
}
Old question, already answered, I know.
However, nobody seems to mention the Expression class.
Granted, this might not fix your problem because your question leaves it ambiguous as to where in the SQL the Raw condition needs to be included (is it in the SELECT statement or in the WHERE statement?). However, this piece of information you might find useful regardless.
Include the following class in your Model file:
use Illuminate\Database\Query\Expression;
Then inside the Model class define a new variable
protected $select_cols = [
'id', 'name', 'foo', 'bar',
Expression ('(select count(1) from sub_table where sub_table.x = top_table.x) as my_raw_col'), 'blah'
]
And add a scope:
public function scopeMyFind ($builder, $id) {
return parent::find ($id, $this->select_cols);
}
Then from your controller or logic-file, you simply call:
$rec = MyModel::myFind(1);
dd ($rec->id, $rec->blah, $rec->my_raw_col);
Happy days.
(Works in Laravel framework 5.5)
use Eloquent Model related to the query you're working on.
and do something like this:
$contactus = ContactUS::select('*')
->whereRaw('id IN (SELECT min(id) FROM users GROUP BY email)')
->orderByDesc('created_at')
->get();
You could shorten your result handling by writing
$objects = new Collection(array_map(function($entry) {
return (new static())->setRawAttributes((array) $entry, true);
}, $result));
if you want to select info it is DB::select(Statement goes here) just remember that some queries wont work unless you go to Config/Database.php and set connections = mysql make sure 'strict' = false
Just know that it can cause some security concerns
if ever you might also need this.
orderByRaw() function for your order by.
Like
WodSection::orderBy('score_type')
->orderByRaw('FIELD(score_type,"score_type") DESC')
->get();

Categories