use "()" in Fuelphp query builder - php

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')

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!

multiple select with inner join

I try to translate this simple SQL request with Doctrine but with no success:
SELECT `groups`.`departure`, COUNT(`group_users`.`group_id`) as 'teamLength' FROM `groups` INNER JOIN `group_users` WHERE `groups`.`id` = `group_users`.`group_id` AND departure BETWEEN '2017-03-01' AND '2017-03-31'
Currently, my code looks like:
public function findDatesBetween(\AppBundle\Entity\Itinerary $itinerary, \DateTime $firstDate, \DateTime $lastDate)
{
$q = $this->createQueryBuilder('g')
->select('g', 'COUNT(gu.group) teamLength')
->innerJoin('AppBundle:GroupUser', 'gu', Join::WITH, 'g.id = gu.group')
->andWhere('g.itinerary = :itineraryId')
->setParameter('itineraryId', $itinerary->getId())
->andWhere('g.departure BETWEEN :firstDate AND :lastDate')
->setParameter('firstDate', $firstDate->format('Y-m-d'))
->setParameter('lastDate', $lastDate->format('Y-m-d'));
return ($q->getQuery()->getResult());
}
If I change the select() like this : ->select('g'), it works. But I need to get COUNT(gu.group).
With the actual code, I have an exception when I try to use departure: Call to a member function getDeparture() on array
How can I fix it ?
Thank you.
You want to get an aggregate result with your query, that means that your result will be an associative array and not an array of entities instances.
Try to get your data with the getArrayResult method and access your data accordingly.
You can do a {{ dump(data) }} on your twig view to see the structure of your result.

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();

How do I get the query builder to output its raw SQL query as a string?

Given the following code:
DB::table('users')->get();
I want to get the raw SQL query string that the database query builder above will generate. In this example, it would be SELECT * FROM users.
How do I do this?
Use the toSql() method on a QueryBuilder instance.
DB::table('users')->toSql() would return:
select * from `users`
This is easier than wiring up an event listener, and also lets you check what the query will actually look like at any point while you're building it.
Note: This method works for query builder or Eloquent, however toSql() is used instead of first() or get(). You cannot run the query and also get the SQL at the same time using this method.
To output to the screen the last queries ran you can use this:
\DB::enableQueryLog(); // Enable query log
// Your Eloquent query executed by using get()
dd(\DB::getQueryLog()); // Show results of log
I believe the most recent queries will be at the bottom of the array.
You will have something like that:
array(1) {
[0]=>
array(3) {
["query"]=>
string(21) "select * from "users""
["bindings"]=>
array(0) {
}
["time"]=>
string(4) "0.92"
}
}
(Thanks to Joshua's comment below.)
DB::QueryLog() works only after you execute the query using $builder->get().
If you want to get the raw query before or without executing the query, you can use the $builder->toSql() method.
Example to get the raw SQL and to replace '?' with actual binding values:
$query = str_replace(array('?'), array('\'%s\''), $builder->toSql());
$query = vsprintf($query, $builder->getBindings());
dump($query);
$result = $builder->get();
Or you can deliberately trigger an error, for example, by using a non-existent table or column. Then you can see the generated query in the exception message.
You can listen to the 'illuminate.query' event. Before the query add the following event listener:
Event::listen('illuminate.query', function($query, $params, $time, $conn)
{
dd(array($query, $params, $time, $conn));
});
DB::table('users')->get();
This will print out something like:
array(4) {
[0]=>
string(21) "select * from "users""
[1]=>
array(0) {
}
[2]=>
string(4) "0.94"
[3]=>
string(6) "sqlite"
}
If you are trying to get the Log using Illuminate without Laravel use:
\Illuminate\Database\Capsule\Manager::getQueryLog();
You could also nock up a quick function like so:
function logger()
{
$queries = \Illuminate\Database\Capsule\Manager::getQueryLog();
$formattedQueries = [];
foreach ($queries as $query) :
$prep = $query['query'];
foreach ($query['bindings'] as $binding) :
if (is_bool($binding)) {
$val = $binding === true ? 'TRUE' : 'FALSE';
} else if (is_numeric($binding)) {
$val = $binding;
} else {
$val = "'$binding'";
}
$prep = preg_replace("#\?#", $val, $prep, 1);
endforeach;
$formattedQueries[] = $prep;
endforeach;
return $formattedQueries;
}
EDIT
updated versions seem to have query logging disabled by default (the above returns an empty array). To turn back on, when initialising the Capsule Manager, grab an instance of the connection and call the enableQueryLog method
$capsule::connection()->enableQueryLog();
EDIT AGAIN
Taking the actual question into consideration, you could actually do the following to convert the current single query instead of all previous queries:
$sql = $query->toSql();
$bindings = $query->getBindings();
There is a method in eloquent for getting query string.
toSql()
in our case,
DB::table('users')->toSql();
return
select * from users
is the exact solution that return the SQL query string..Hope this helpful...
$data = User::toSql();
echo $data; //this will retrun select * from users. //here User is model
This is the far best solution I can suggest to any one for debug-ing eloquent last query or final query although this has been discussed as well:
// query builder
$query = DB::table('table_name')->where('id', 1);
// binding replaced
$sql = str_replace_array('?', $query->getBindings(), $query->toSql());
// for laravel 5.8^
$sql = Str::replaceArray('?', $query->getBindings(), $query->toSql());
// print
dd($sql);
If you use laravel 5.1 and MySQL you can use this function made by me:
/*
* returns SQL with values in it
*/
function getSql($model)
{
$replace = function ($sql, $bindings)
{
$needle = '?';
foreach ($bindings as $replace){
$pos = strpos($sql, $needle);
if ($pos !== false) {
if (gettype($replace) === "string") {
$replace = ' "'.addslashes($replace).'" ';
}
$sql = substr_replace($sql, $replace, $pos, strlen($needle));
}
}
return $sql;
};
$sql = $replace($model->toSql(), $model->getBindings());
return $sql;
}
As an input parameter you can use either of these
Illuminate\Database\Eloquent\Builder
Illuminate\Database\Eloquent\Relations\HasMany
Illuminate\Database\Query\Builder
First You will need to enable the query log by calling:
DB::enableQueryLog();
after queries using the DB facade you can write:
dd(DB::getQueryLog());
the output will like below:
array:1 [▼
0 => array:3 [▼
"query" => "select * from `users` left join `website_user` on `users`.`id` = `website_user`.`user_id` left join `region_user` on `users`.`id` = `region_user`.`user_id` left ▶"
"bindings" => array:5 [▶]
"time" => 3.79
]
]
A 'macroable' replacement to get the SQL query with the bindings.
Add below macro function in AppServiceProvider boot() method.
\Illuminate\Database\Query\Builder::macro('toRawSql', function(){
return array_reduce($this->getBindings(), function($sql, $binding){
return preg_replace('/\?/', is_numeric($binding) ? $binding : "'".$binding."'" , $sql, 1);
}, $this->toSql());
});
Add an alias for the Eloquent Builder. (Laravel 5.4+)
\Illuminate\Database\Eloquent\Builder::macro('toRawSql', function(){
return ($this->getQuery()->toRawSql());
});
Then debug as usual. (Laravel 5.4+)
E.g. Query Builder
\Log::debug(\DB::table('users')->limit(1)->toRawSql())
E.g. Eloquent Builder
\Log::debug(\App\User::limit(1)->toRawSql());
Note: from Laravel 5.1 to 5.3, Since Eloquent Builder doesn't make use of the Macroable trait, cannot add toRawSql an alias to the Eloquent Builder on the fly. Follow the below example to achieve the same.
E.g. Eloquent Builder (Laravel 5.1 - 5.3)
\Log::debug(\App\User::limit(1)->getQuery()->toRawSql());
First way:
Simply you can do following stuff using toSql() method,
$query = DB::table('users')->get();
echo $query->toSql();
If it's not working you can set-up the thing from laravel documentation.
Second way:
Another way to do it is
DB::getQueryLog()
but if it's returns an empty array then by default it's disabled visit this,
just enable with DB::enableQueryLog() and it will work :)
for more info visit Github Issue to know more about it.
Hope it helps :)
As of Laravel 5.8.15 the query builder now has dd and dump methods so you can do
DB::table('data')->where('a', 1)->dump();
There's a lot of information already answered, will just post my own findings that i've been using whenever i need to output the sql query before it's being executed.
Consider below sample:
$user = DB::table('user')->where('id',1);
echo $user->toSql();
echo $user->toSql() = This will just out put the raw query but will not show the parameter(s) passed.
To output the query with the parameter being passed we can use laravel getBindings() and helper str_replace_array like this:
$queryWithParam = str_replace_array('?',$user->getBindings(),$user->toSql());
echo $queryWithParam;
Hope this also helps.
In my opinion, this will be the best approach as a beginner:
echo "<pre>";
print_r($query->toSql());
print_r($query->getBindings());
This is also depicted here.
https://stackoverflow.com/a/59207557/9573341
The most easiest way is to make deliberate mistake. For example, I want to see the full SQL query of the following relation:
public function jobs()
{
return $this->belongsToMany(Job::class, 'eqtype_jobs')
->withPivot(['created_at','updated_at','id'])
->orderBy('pivot_created_at','desc');
}
I just to make a column to be not found, here I choose created_at and I changed it to created_ats by adding trailing s to be:
public function jobs()
{
return $this->belongsToMany(Job::class, 'eqtype_jobs')
->withPivot(['created_ats','updated_at','id'])
->orderBy('pivot_created_at','desc');
}
So, the debuger will return the following error:
(4/4) ErrorException SQLSTATE[42S22]: Column not found: 1054 Unknown
column 'eqtype_jobs.created_ats' in 'field list' (SQL: select
jobs.*, eqtype_jobs.set_id as pivot_set_id,
eqtype_jobs.job_id as pivot_job_id, eqtype_jobs.created_ats
as pivot_created_ats, eqtype_jobs.updated_at as
pivot_updated_at, eqtype_jobs.id as pivot_id from jobs inner
join eqtype_jobs on jobs.id = eqtype_jobs.job_id where
eqtype_jobs.set_id = 56 order by pivot_created_at desc limit 20
offset 0) (View:
/home/said/www/factory/resources/views/set/show.blade.php)
The above error message returns the full SQL query with the mistake
SQL: select jobs.*, eqtype_jobs.set_id as pivot_set_id, eqtype_jobs.job_id as pivot_job_id, eqtype_jobs.created_ats as pivot_created_ats, eqtype_jobs.updated_at as pivot_updated_at, eqtype_jobs.id as pivot_id from jobs inner join eqtype_jobs on jobs.id = eqtype_jobs.job_id where eqtype_jobs.set_id = 56 order by pivot_created_at desc limit 20 offset 0
Now, just remove the extra s from created_at and test this SQL as you like in any SQL editor such as phpMyAdmin SQL editor!
###Notice:
The solution has been tested with Laravel 5.4.
Add this function to your application and simply call.
function getQuery($sql){
$query = str_replace(array('?'), array('\'%s\''), $sql->toSql());
$query = vsprintf($query, $sql->getBindings());
return $query;
}
Output: "select * from user where lang = 'en' and status = '1' order by updated_at desc limit 25 offset 0"
use debugbar package
composer require "barryvdh/laravel-debugbar": "2.3.*"
From laravel 5.2 and onward. you can use DB::listen to get executed queries.
DB::listen(function ($query) {
// $query->sql
// $query->bindings
// $query->time
});
Or if you want to debug a single Builder instance then you can use toSql method.
DB::table('posts')->toSql();
To See Laravel Executed Query use laravel query log
DB::enableQueryLog();
$queries = DB::getQueryLog();
You can use toSql method - the easiest way
DB::table('users')->toSql();
And also if you have bindings in your query and want to see the query with bindings. You cant use somthing like that:
$query = DB::table('table')->whereIn('some_field', [1,2,30]);
$sql_with_bindings = str_replace_array('?', $query->getBindings(), $query->toSql());
dd($sql_with_bindings);
This is the function, I placed in my base model class. Simply pass the query builder object into it and the SQL string will be returned.
function getSQL($builder) {
$sql = $builder->toSql();
foreach ( $builder->getBindings() as $binding ) {
$value = is_numeric($binding) ? $binding : "'".$binding."'";
$sql = preg_replace('/\?/', $value, $sql, 1);
}
return $sql;
}
In order to log all the executed queries you can use DB::enableQueryLog() icw DB::getQueryLog(). The output has the structure below.
[
[
"query" => "select * from "users" where name = ?"
"bindings" => ["John Doe"]
"time" => 0.34
],
...
]
Furthermore, I combined some answers here in order to get the perfect function to parse the sql with the compiled bindings. See below. I even created a custom Builder class implementing this functionality in order to do e.g. User::where('name','John Doe')->parse();
function parse_sql(string $sql, array $bindings) : string
{
$compiled_bindings = array_map('compile_binding', $bindings);
return preg_replace_array("/\?/", $compiled_bindings, $sql);
}
function compile_binding($binding)
{
$grammar = new MySqlGrammar;
if (is_bool($binding))
{
return (int)$binding; //This line depends on the database implementation
}
if(is_string($binding))
{
return "'$binding'";
}
if ($binding instanceof DateTimeInterface)
{
return $binding->format($grammar->getDateFormat());
}
return $binding;
}
Try this:
$results = DB::table('users')->toSql();
dd($results);
Note: get() has been replaced with toSql() to display the raw SQL query.
For laravel 5.5.X
If you would like to receive each SQL query executed by your application, you may use the listen method. This method is useful for logging queries or debugging. You may register your query listener in a service provider:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
DB::listen(function ($query) {
// $query->sql
// $query->bindings
// $query->time
});
}
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
//
}
}
Source
As much as I love this framework, I hate when it acts like crap.
DB::enableQueryLog() is totally useless. DB::listen is equally useless. It showed part of the query when I said $query->count(), but if I do $query->get(), it has nothing to say.
The only solution that appears to work consistently is to intentionally put some syntax or other error in the ORM parameters, like an nonexistent column/table name, run your code on the command line while in debug mode, and it will spit out the SQL error with the full frickin' query finally. Otherwise, hopefully the error appears in the log file if ran from the web server.
You can use this package for get all the queries which are executing when you load your page
https://github.com/barryvdh/laravel-debugbar
Print last query
DB::enableQueryLog();
$query = DB::getQueryLog();
$lastQuery = end($query);
print_r($lastQuery);
If you are using tinker and want to log the SQL query formed you can do
$ php artisan tinker
Psy Shell v0.9.9 (PHP 7.3.5 — cli) by Justin Hileman
>>> DB::listen(function ($query) { dump($query->sql); dump($query->bindings); dump($query->time); });
=> null
>>> App\User::find(1)
"select * from `users` where `users`.`id` = ? limit 1"
array:1 [
0 => 1
]
6.99
=> App\User {#3131
id: 1,
name: "admin",
email: "admin#example.com",
created_at: "2019-01-11 19:06:23",
updated_at: "2019-01-11 19:06:23",
}
>>>
My way of doing this, based on the log view, only needs to modify the file app/Providers/AppServiceProvider.php:
Add this code into app/Providers/AppServiceProvider.php
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
//
DB::listen(function ($query) {
$querySql = str_replace(['?'], ['\'%s\''], $query->sql);
$queryRawSql = vsprintf($querySql, $query->bindings);
Log::debug('[SQL EXEC]', [
"raw sql" => $queryRawSql,
"time" => $query->time,
]
);
});
}
My sql handle code :
$users = DB::table('users')
->select(DB::raw('count(*) as user_count, username '))
->where('uid', '>=', 10)
->limit(100)
->groupBy('username')
->get()
;
dd($users);
See log storage/logs/laravel-2019-10-27.log :
[2019-10-27 17:39:17] local.DEBUG: [SQL EXEC] {"raw sql":"select count(*) as user_count, username from `users` where `uid` >= '10' group by `username` limit 100","time":304.21}

Grouping WHERE clauses in Codeigniter

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!

Categories