Chaining orX in Doctrine2 query builder - mysql

I have to dynamically add OR expressions to the query builder returned by getListQueryBuilder, right after adding a where clause. I can't find any suitable way of doing this, i'm just started learning Doctrine.
How can i "chain" a given number of orX and add them to my builder?
public function getListQueryBuilder($ownerId)
{
$qb = $this->createQueryBuilder('t');
return $qb
->where($qb->expr()->eq('t.user', ':user'))
->setParameter('user', $ownerId);
}
$builder = getListQueryBuilder(4);
// $ORs is a dynamically builded array, here is just an example
$ORs = array();
$ORs[] = $builder->expr()->like("t.name", 'my name');
$ORs[] = $builder->expr()->like("t.description", 'desc');
// Adding ORs to the builder
$builder->andWhere($builder->expr()->orX(/* here */));

You can check this solution:
$orX = $builder->expr()->orX();
foreach($ORs as $or) {
$orX->add($or);
}
$builder->andWhere($orX);

I stumbled on the same problem and tried :
$builder->andWhere($builder->expr()->orX($ORs));
but it does not work since orX calls "return new Expr\Orx(func_get_args());" internally and you end up with something like array(array(or1, or2))
having looked at the API however i figured you can do this:
$builder->andWhere($builder->expr()->orX()->addMultiple($ORs));
OR do use $ORs table at all but issue:
$orx = $builder->expr()->orX();
$orx->add($builder->expr()->like("t.name", 'my name'));
$orx->add($builder->expr()->like("t.description", 'desc'));
$builder->andWhere($orx);

Related

Codeigniter 4 Subquery whereIn function BaseBuilder not working

I have tried the Codeigniter 4 subquery several ways and I am now trying a method from post stackoverflow.com/questions/66795533/writing-sql-subquery-with-ci4-active-record but not working. Receiving the below error. I tried to change the type as specified below but no good.
TypeError
App\Models\Mbox\Mbox_model::App\Models\Mbox{closure}(): Argument #1 ($subqueryBuilder) must be of type App\Models\Mbox\BaseBuilder,
Below is my model and query.
namespace App\Models\Mbox;
use CodeIgniter\Model;
class Mbox_model extends Model
{
public function get_messages($uid,$box)
{
$db = \Config\Database::connect();
$builder = $db->table('messages');//->fromSubquery($subquery, 'mc');
$builder->select('messages.*,users.*,conversations.created_on,conversations.fromuser,
conversations.read, conversations.message')
->join('conversations', 'conversations.mid = messages.mid')
->join('users', 'users.id = conversations.fromuser')
->where('messages.owner', $uid)
->where('mailbox', $box)
->whereIn('conversations.cid', function (BaseBuilder $subqueryBuilder)
{
return $subqueryBuilder->selectMax('mc.cid')
->from('conversations mc')
->join('messages', 'messages.owner = mc.touser')
->where('messages.owner',$uid)
->where('messages.mailbox', $box)
->groupBy('mc.fromuser');
})->orderBy('created_on', 'DESC');
Resolved. You must include use CodeIgniter\Database\BaseBuilder;
I also had to add use the anonymous function as described in a Codeigniter forum to pass parameters and now the query looks good.
$db = \Config\Database::connect();
$builder = $db->table('messages');
$builder->select('messages.*,users.*,conversations.created_on,conversations.fromuser,
conversations.read, conversations.message')
->join('conversations', 'conversations.mid = messages.mid')
->join('users', 'users.id = conversations.fromuser')
->whereIn('conversations.cid', function (BaseBuilder $subqueryBuilder) use ($uid,$box)
{
return $subqueryBuilder->selectMax('mc.cid')
->from('conversations mc')
->join('messages', 'messages.owner = mc.touser')
->where('messages.owner', $uid)
->where('messages.mailbox', $box)
->groupBy('mc.fromuser');
})
->where('messages.owner', $uid)
->where('mailbox', $box)
->orderBy('created_on', 'DESC');

How to fetch data from database with 3 parameters Laravel

I'm still new to this laravel, for now I'm facing a trouble for fetching data from the database. What i want to get is when there are only one data available, the second parameters won't be executed, but if there are some data available on the second parameters, then all the data from first parameter and the second parameter will be called.
$detail = Barang_Keluar_Detail::findOrFail($id); //15
$cariid = $detail->pluck('barang_keluar_id');
$instansiquery = Barang_Keluar::where('id',$cariid)->first(); //21
$instansiid = $instansiquery->pluck('instansi_id');
$tanggal = $instansiquery->pluck('tanggal')->first();//2019-12-31
and the parameter are here
$cariinstasama = Barang_Keluar::where('id', $cariid)
->orWhere(function ($query) use($instansiid, $tanggal) {
$query->where('tanggal', "'$tanggal'")
->where('instansi_id', $instansiid);
});
Please any help will be appreciated, thank you.
Laravel query builder provides elegant way to put conditional clause using when() . You can put conditional clause on your query like this:
$cariinstasama = Barang_Keluar::where('id', $cariid)
->when($instansiid, function ($query, $instansiid) {
return $query->where('instansi_id', $instansiid);
})
->when($tanggal, function ($query, $tanggal) {
return $query->where('tanggal', $tanggal);
})->get();
For more info see https://laravel.com/docs/5.8/queries#conditional-clauses
You can try this as well.
$cariinstasama = Barang_Keluar::where('id', $cariid);
if($instansiid !== null)
{
$cariinstasama->where('instansi_id', $instansiid);
}
if($tanggal !== null)
{
$cariinstasama->where('instansi_id', $instansiid);
}
$result = $cariinstasama->get();
Its not clear what exactly you want.
Are you applying more than one parameter on the query if the first parameter result gives you more than one row in the database? If yes check out my approach :
$query = new Model(); // the model you want to query
if($query->where('col1', $param1)->count() > 1) // checks if the query from the 1st parameter produces more than one row in the database
$query = $query->where( // if yes apply more parameters to the query
[
['col1', $param1],
['col2', $param2]
]
);
else
$query = $query->where('col1', $param1);
$results = $query->get();
Hope it helps....

How do I use the between() after find() [duplicate]

Is it possible to do a "BETWEEN ? AND ?" where condition LIKE in cakephp 2.5?
In cakephp 2.5 I write something like
'conditions' => ['start_date BETWEEN ? AND ?' => ['2014-01-01', '2014-12-32']]
how can I migrate that?
additionally I would write something like
'conditions' => [ '? BETWEEN start_date AND end_date'] => '2014-03-31']
Expressions
Between expression are supported out of the box, however they only support the first case without additional fiddling:
$Query = $Table
->find()
->where(function($exp) {
return $exp->between('start_date', '2014-01-01', '2014-12-32', 'date');
});
If you'd wanted to handle the second case via the between method, then you'd have to pass all values as expressions, which can easily go wrong, as they will not be subject to escaping/parameter binding in that case, you'd have to do that on your own (which is anything but recommended! See the security notes in the manual for PDO::quote()), something along the lines of:
use Cake\Database\Expression\IdentifierExpression;
use Cake\Database\Expression\QueryExpression;
use Cake\ORM\Query;
// ...
$Query = $Table
->find()
->where(function(QueryExpression $exp, Query $query) {
return $exp->between(
$query->newExpr(
$query->connection()->driver()->quote(
'2014-03-31',
\PDO::PARAM_STR
)
),
new IdentifierExpression('start_date'),
new IdentifierExpression('end_date')
);
});
That might feel a little inconvenient for such a basic SQL expression that is supported by all SQL dialects that CakePHP ships with, so you may have a reason here to use a raw SQL snippet with value bindig instead.
It should be noted however that expressions are often the better choice when it comes to for example cross dialect support, as they can be (more or less) easily transformed at compile time, see the implementations of SqlDialectTrait::_expressionTranslators(). Also expressions usually support automatic identifier quoting.
Value binding
Via manual value binding you can pretty much create anything you like. It should however be noted that whenever possible, you should use expressions instead, as they are easier to port, which happens out of the box for quite a few expressions already.
$Query = $Table
->find()
->where([
'start_date BETWEEN :start AND :end'
])
->bind(':start', '2014-01-01', 'date')
->bind(':end', '2014-12-31', 'date');
That way the second case can also be solved very easily, like:
$Query = $Table
->find()
->where([
':date BETWEEN start_date AND end_date'
])
->bind(':date', '2014-03-31', 'date');
A mixture of both (safest and most compatible approach)
It's also possible to mix both, ie use an expression that makes use of custom bindings, something along the lines of this:
use Cake\Database\Expression\IdentifierExpression;
use Cake\Database\Expression\QueryExpression;
use Cake\ORM\Query;
// ...
$Query = $Table
->find()
->where(function(QueryExpression $exp, Query $query) {
return $exp->between(
$query->newExpr(':date'),
new IdentifierExpression('start_date'),
new IdentifierExpression('end_date')
);
})
->bind(':date', '2014-03-31', 'date');
That way you could handle the second case using possibly portable expressions, and don't have to worry about quoting/escaping input data and identifiers manually.
Regular comparison using array syntax
All that being said, in the end BETWEEN is just the same as using two separate simple conditions like this:
$Query = $Table
->find()
->where([
'start_date >=' => '2014-01-01',
'start_date <=' => '2014-12-32',
]);
$Query = $Table
->find()
->where([
'start_date >=' => '2014-03-31',
'end_date <=' => '2014-03-31',
]);
But don't be mad, if you read all the way down to here, at least you learned something about the ins and outs of the query builder.
See also
Cookbook > Database Access & ORM > Query Builder > Advanced Conditions
API > \Cake\Database\Query::bind()
Currently there seems to be only two options. The core now supports this out of the box, the following is just kept for reference.
Value binding (via the database query builder)
For now the ORM query builder (Cake\ORM\Query), the one that is being retrived when invoking for example find() on a table object, doesn't support value binding
https://github.com/cakephp/cakephp/issues/4926
So, for being able to use bindings you'd have to use the underlying database query builder (Cake\Database\Query), which can for example be retrived via Connection::newQuery().
Here's an example:
$conn = ConnectionManager::get('default');
$Query = $conn->newQuery();
$Query
->select('*')
->from('table_name')
->where([
'start_date BETWEEN :start AND :end'
])
->bind(':start', new \DateTime('2014-01-01'), 'date')
->bind(':end', new \DateTime('2014-12-31'), 'date');
debug($Query->execute()->fetchAll());
This would result in a query similar to this
SELECT
*
FROM
table_name
WHERE
start_date BETWEEN '2014-01-01' AND '2014-12-31'
A custom expression class
Another option would be a custom expression class that generates appropriate SQL snippets. Here's an example.
Column names should be wrapped into identifier expression objects in order to them be auto quoted (in case auto quoting is enabled), the key > value array syntax is for binding values, where the array key is the actual value, and the array value is the datatype.
Please note that it's not safe to directly pass user input for column names, as they are not being escaped! Use a whitelist or similar to make sure the column name is safe to use!
Field between values
use App\Database\Expression\BetweenComparison;
use Cake\Database\Expression\IdentifierExpression;
// ...
$between = new BetweenComparison(
new IdentifierExpression('created'),
['2014-01-01' => 'date'],
['2014-12-31' => 'date']
);
$TableName = TableRegistry::get('TableName');
$Query = $TableName
->find()
->where($between);
debug($Query->execute()->fetchAll());
This would generate a query similar to the one above.
Value between fields
use App\Database\Expression\BetweenComparison;
use Cake\Database\Expression\IdentifierExpression;
// ...
$between = new BetweenComparison(
['2014-03-31' => 'date'],
new IdentifierExpression('start_date'),
new IdentifierExpression('end_date')
);
$TableName = TableRegistry::get('TableName');
$Query = $TableName
->find()
->where($between);
debug($Query->execute()->fetchAll());
This on the other hand would result in a query similar to this
SELECT
*
FROM
table_name
WHERE
'2014-03-31' BETWEEN start_date AND end_date
The expression class
namespace App\Database\Expression;
use Cake\Database\ExpressionInterface;
use Cake\Database\ValueBinder;
class BetweenComparison implements ExpressionInterface {
protected $_field;
protected $_valueA;
protected $_valueB;
public function __construct($field, $valueA, $valueB) {
$this->_field = $field;
$this->_valueA = $valueA;
$this->_valueB = $valueB;
}
public function sql(ValueBinder $generator) {
$field = $this->_compilePart($this->_field, $generator);
$valueA = $this->_compilePart($this->_valueA, $generator);
$valueB = $this->_compilePart($this->_valueB, $generator);
return sprintf('%s BETWEEN %s AND %s', $field, $valueA, $valueB);
}
public function traverse(callable $callable) {
$this->_traversePart($this->_field, $callable);
$this->_traversePart($this->_valueA, $callable);
$this->_traversePart($this->_valueB, $callable);
}
protected function _bindValue($value, $generator, $type) {
$placeholder = $generator->placeholder('c');
$generator->bind($placeholder, $value, $type);
return $placeholder;
}
protected function _compilePart($value, $generator) {
if ($value instanceof ExpressionInterface) {
return $value->sql($generator);
} else if(is_array($value)) {
return $this->_bindValue(key($value), $generator, current($value));
}
return $value;
}
protected function _traversePart($value, callable $callable) {
if ($value instanceof ExpressionInterface) {
$callable($value);
$value->traverse($callable);
}
}
}
You can use one of following 2 methods.
Method 1 :
$start_date = '2014-01-01 00:00:00';
$end_date = '2014-12-31 23:59:59';
$query = $this->Table->find('all')
->where(function ($exp, $q) use($start_date,$end_date) {
return $exp->between('start_date', $start_date, $end_date);
});
$result = $query->toArray();
Method 2:
$start_date = '2014-01-01 00:00:00';
$end_date = '2014-12-31 23:59:59';
$query = $this->Table->find('all')
->where([
'start_date BETWEEN :start AND :end'
])
->bind(':start', new \DateTime($start_date), 'datetime')
->bind(':end', new \DateTime($end_date), 'datetime');
$result = $query->toArray();
I'm using it like this
$this->Table->find()->where(['data_inicio BETWEEN '.'\''.$data_inicio.'\''.' AND .'\''.$data_final.'\''.' ']);
Hello guys please use this query to get data on the basis of range of value
$query = $this->Leads->find('all',
array('conditions'=>array('postcode BETWEEN '.$postcodeFrom.' and'.$postcodeTo.''), 'recursive'=>-1));
debug($query);
print_r($query->toArray());

Laravel Select Query within helper file

I'm coming from codeigniter background. Unlike codeigniter helper directory, i just created helper directory within app directory of Laravel. Just want to know how to execute query within this common function. Here is my codeigniter function.
function show_menu($primary_key_col, $parent_id, $sort_order)
{
$output = "";
$ci =& get_instance();
$ci->db->select("*");
$ci->db->where('is_active', "Y");
$ci->db->where('is_delete', "N");
$ci->db->where('parent_id', $parent_id);
($sort_order!="")?$ci->db->order_by($sort_order, "ASC"):"";
$query = $ci->db->get('tbl_cms_menus');
foreach ($query->result() as $row){
$output .= '<option value="'.$row->$primary_key_col.'">'.$indent.$row->menu_name.'</option>';
}
return $output;
}
I tried something like this in laravel file. but this code did't give me any result. Please tell me where i'm doing wrong in this code. thanks
function databaseTable()
{
$table = DB::table('tbl_cms_menus');
$get_rows = $table->get();
$count_rows = $table->count();
if($count_rows > 0){
foreach ($get_rows as $tbl)
{
echo $tbl->menu_name;
}
}
}
This code will rot so hard that it shipped pre-rotten.
But, if you want to just.. ram it into the app all dry like that.. then add something like this to your base controller class...
$whatever = crazyChainingStuff;
foreach ($whatever ...) { $topMenu .= ... }
View::share('topMenu', $topMenu);
If you want to learn how to write code that will do less damage to your company and your clients then I recommend starting by watching Uncle Bob's "Fundamentals" videos. At least the first 5-6. http://cleancoders.com
It looks like you are trying to generate a drop-down/select with some data from your database, in this case, you should pass the data required for the drop-down/select from your controller to the view where you have written your HTML, for example, in your view, you may have a select like this:
echo Form::select('cms_menu', $cms_menu, Input::old('cms_menu'));
Or this (If you are using Blade):
{{ Form::select('cms_menu', $cms_menu, Input::old('cms_menu')) }}
From your controller you should pass the $cms_menu which should contain the menu-items as an arrtay and to populate that array you may try something like this:
$menuItems = DB::table('tbl_cms_menus')->lists('menu_name','id');
return View::make('your_view_name', array('cms_menu' => $menuItems));
Also, you may use something like this:
// Assumed you have a Page model
$menuItems = Page::lists('menu_name', 'id');
return View::make('your_view_name', array('cms_menu' => $menuItems));
You may also read this article which is about building a menu from database using view composer (More Laravelish way). Read more about Form::select on documentation.
It was too late to give an answer. I was also from CodeIgniter background and when I learnt Laravel then first I try to find how can I write a query in Helper. My Team leader helped me.
I have converted your code in a helper function.
function show_menu($primary_key_col, $parent_id, $sort_order)
{
$query = DB::table('tbl_cms_menus')
->select('*')
->where('is_active', '=', 'Y')
->where('is_delete', '=', 'N')
->where('parent_id', '=', $parent_id);
($sort_order != "")? $query->orderBy($sort_order, "ASC") : "";
$resultData = $query->get()->toArray();
}
Here $resultData will be array format. Now, you can create a foreach loop according to your requirement.

Troubles with IN in Yii query builder

I use Yii Framework and i need to build difficult query with many conditions.
I'm filling 2 arrays $conditions and $values.
And i have one problem.
Below is example
When i use
$usersId = '1,2';
$conditions[] = 'e.user_id IN(:usersId)';
$values[':usersId'] = $usersId;
I get only value from user_id = 1
When i'm not use option and write manually
$usersId = '1,2';
$conditions[] = 'e.user_id IN(' . $usersId . ')';
no problem.
Of course i can use second construction, but it seems not very good.
You should addInCondition
$criteria->addInCondition('e.user_id',array(1,2));
Yii way would be to use CDbCriteria addInCondition function
$usersId = array(1,2); //must be array
$criteria=new CDbCriteria();
$criteria->addInCondition('user_id',$usersId);
$result = MyModel::model()->findAll($criteria);
$values[':usersId'] = $usersId;
If I understand your wuestion correctly, you can use the BindParam function in yii?
Instead of this - $values[':usersId'] = $usersId;
Write this - $command->BindParam(':usersId', $usersId, PDO::PARAM_STR);
Very simply, you're binding your parameters to your command statement.
Hope it works!