Dynamic query ORM Laravel 4.2 - php

Alright I have a GeneralModel written in CodeIgniter and a friend asked me if I could convert it into Laravel 4.2 for him. I was working on this and I think I have most of it correct but I am getting stuck at the select statement.
In CodeIgniter I have the following:
public function getData($table, $multiple = 1, $field = FALSE, $val = FALSE){
if($field != FALSE){
// WHERE in case of FIELD / VAL :)
$this->db->where($field, $val);
}
$query = $this->db->get($table);
if($multiple == 1){
// Multiple rows
return $query->result_array();
} else {
// One row
return $query->row_array();
}
}
Does anyone here knows how I can convert this function into Laravel 4.2 syntax?
I currently have:
public function getData($table, $multiple = 1, $field = FALSE, $val = FALSE){
$result = DB::table($table);
}
I got stuck pretty quickly since I have no idea how I can achieve the same in Laravel 4.2 with splitting up the sections of the query like I did with CodeIgniter.

You can chain methods in the same way:
public function getData($table, $multiple = 1, $field = FALSE, $val = FALSE)
{
$query = DB::table($table);
if ($field != FALSE) {
// WHERE in case of FIELD / VAL :)
$query->where($field, $val);
}
if ($multiple)
return $query->get();
else
return $query->first();
}

Laravel is similar in that you can use the Fluent Query Builder to build your queries in multiple stages prior to actually making the query. Once you know this, the translation is pretty straightforward:
public function getData($table, $multiple = 1, $field = FALSE, $val = FALSE)
{
$query = DB::table($table);
if($field){
// WHERE in case of FIELD / VAL :)
$query = $query->where($field, $val);
}
if($multiple) {
return $query->get();
}
return $query->first();
}
I don't think it's really good practice relying on Fluent though inside of an Eloquent model, but there are cases where that can't be helped. If the current objective is to convert the project to Laravel, there's probably calling code relying on the fact that this method exists. Converting the function to use Eloquent rather than Fluent will change the function's signature and cause other parts of the code to break, but it would look like this:
public function getData($multiple = true, $field = false, $val = false)
{
$query = $this;
if($field) {
$query = $query->where($field, $val);
}
if($multiple) {
return $query->get();
}
return $query->first();
}
The calling code itself can be modified to do the exact same function in Laravel like this:
// Instead of...
$result = $model->getData(1, 'field', 'value');
// You can do this:
$result = $model->where('field', 'value')->get();
// Or this if you'd rather not have multiple:
$result = $model->where('field', 'value')->first();
Using this function inside Eloquent (IMHO) in the long run doesn't really save you much, and instead is mostly just clutter.

Related

Reference- what is wrong with my codeigniter code

hello everyone i write a codeigniter function to return data fro database
this is my function
public function get_total_results($filtering = false)
{
if ($filtering) {
$this->get_filtering();
}
foreach ($this->joins as $val) {
$this->ci->db->join($val[0], $val[1], $val[2]);
}
foreach ($this->where as $val) {
$this->ci->db->where($val[0], $val[1], $val[2]);
}
foreach ($this->or_where as $val) {
$this->ci->db->or_where($val[0], $val[1], $val[2]);
}
foreach ($this->group_by as $val) {
$this->ci->db->group_by($val);
}
foreach ($this->like as $val) {
$this->ci->db->like($val[0], $val[1], $val[2]);
}
if (strlen($this->distinct) > 0) {
$this->ci->db->distinct($this->distinct);
$this->ci->db->select($this->columns);
}
$query = $this->ci->db->get($this->table, null, null, false);
return $query->num_rows();
}
but i get an error of
An uncaught Exception was encountered
Type: Error
Message: Call to a member function num_rows() on boolean
in line of return $query->num_rows();
i don't know what is wrong with my code that i got this error so i know the error in last line in returning result any suggestion or idea
The problem is that the line
$query = $this->ci->db->get($this->table, null, null, false);
// Note: all you really need is $this->ci->db->get($this->table);
It is assigning the value false to $query. That usually happens when Query Builder creates a SQL statement that does not make sense - it happens.
You can see what Query Builder creates this way
// comment out the get() call
// $this->ci->db->get($this->table);
// run this instead
$sql = $this->db->get_compiled_select($this->table);
echo $sql;
// remove the comments once you see where the problem is
//return $query->num_rows();
You will probably be able to see where the SQL statement syntax is wrong and adjust your earlier code accordingly.
You might want to add a check of the get() return to your logic, e.g.
$query = $this->ci->db->get($this->table);
// Is $query truthy? (not false, null, etc)
if(! empty($query))
{
return $query->num_rows();
}
get() method of codeigniter have syntax like:
get([$table = ''[, $limit = NULL[, $offset = NULL]]])
Parameters: $table (string) – The table to query $limit (int) – The
LIMIT clause $offset (int) – The OFFSET clause
Returns: CI_DB_result instance (method chaining)
Return type: CI_DB_result
You have passed more than three parameters. So, Please check the parameters. It may resolve your problem.
Refer this for more information.

Form with variable number of criterias on Symfony4 / Doctrine2

Let's take https://symfony.com/doc/current/forms.html#building-the-form example form but only for Search in tasks list instead of save.
Goal is to allow searches on task, dueDate or both criterias (in my real case, I have 9 criterias)
Here are src/Repository/ResultRepository.php :
class ResultRepository extends ServiceEntityRepository
{
public function __construct(RegistryInterface $registry)
{
parent::__construct($registry, Result::class);
}
public function findMultiKeys($task, $dueDate): array
{
$qb = $this->createQueryBuilder('d')
->andWhere('d.task = :task')
->setParameter('task', $task)
->andWhere('d.dueDate = :dueDate')
->setParameter('dueDate', $dueDate)
->getQuery();
return $qb->execute();
}
}
It require both criterias to return result(s)!
I ran a lot of searches and find:
How do I use a complex criteria inside a doctrine 2 entity's
repository?
Symfony2/Doctrine QueryBuilder using
andwhere()
Doctrine2 doc - setParameters method
So I code a $where_string variable to construct my variable where.
and a $parameters to construct my variable parameters array, and my findMultiKeys becomes:
public function findMultiKeys($get_form): array
{
$where_string = '';
$parameters = [];
$task = $get_form->getTask();
if ($task !== null) {
$where_string .= "d.task = :task";
$parameters += array('task' => $task);
}
$dueDate = $get_form->getDueDate();
if ($dueDate !== null) {
if ($where_string !== '')
$where_string .= " AND ";
$where_string .= "d.dueDate = :dueDate";
$parameters += array('dueDate' => $dueDate);
}
$qb = $this->createQueryBuilder('d')
->where($where_string)
->setParameters($parameters)
->getQuery();
return $qb->execute();
}
It works, perhaps not the best way?
In my searches, I found, of course, to use ElasticSearch, perhaps to much to my simple need, or I found PetkoparaMultiSearchBundle
Bests solutions welcomes
I have a proposition :-)
You can build an task4Seach entity with your 9 criteria (without
persistance)
create a form type with this "data_class"
use it to get your datas in your repository
$qb = $this->createQueryBuilder('d');
...
if ($task4Seach ->getDueDate()) {
$qb->andWhere($qb->expr()->eq('d.dueDate', ':dueDate'));
$qb->setParameter('dueDate', $task4Seach->getDueDate());
}

Laravel conditions in Controller where clause

I'm trying to build a query based on URL parameters. When the Controller is loaded I need to check which parameters have been provided and build a query from them. It's working with static values, but isn't working with conditional statements. Is my laravel syntax correct?
class OrdenesController extends BaseController {
public function showOrdenes($action)
{
$my_id = Auth::user()->id;
$my_cod = Auth::user()->codprov;
switch ($action)
{
case 'list':
$rows = DB::table('ordens')->count();
if(Input::get("jtSorting"))
{
$search = explode(" ", Input::get("jtSorting"));
$numorden= Input::get("nro_orden");
$filtros =explode(" ", $filtros);
$data = DB::table("ordens")
->select(array('*', DB::raw('SUM(cant_pend) as cant_pend'), DB::raw('SUM(importe) as importe')))
->where('cod_prov', '=', $my_cod)
->where('nro_orden', '=', $numorden)///work
---------- ////no work
if (Input::has('nro_orden')) {
->where('nro_orden', '=', $numorden)
}
---------- /// no work
->groupBy('nro_orden')
->skip(Input::get("jtStartIndex"))
->take(Input::get("jtPageSize"))
->orderBy($search[0], $search[1])
->get();
}
return Response::json(
array(
"Result" => "OK",
"TotalRecordCount" => $rows,
"Records" => $data
)
);
break;
};
}
}
You are missing the variables, no? You haven't told PHP what variable/object to do the where() to in your condition. The magic of Laravel's Eloquent (and a lot of other libraries) is that when you call its methods, it returns itself (the object) back so you can make another method call to it right away.
So when you do this:
$data = DB::table("ordens")
->select(...)
->where(...);
is the same as:
$data = DB::table("ordens");
$data = $data->select(...);
$data = $data->where(...);
But you are trying to do ->where(...) right away after if condition. You need to tell PHP which object/variable you are trying to call the method from. Like this:
$num = Input::get("nro_orden");
$data = DB::table("ordens")
->select(array('*', DB::raw('SUM(cant_pend) as cant_pend'), DB::raw('SUM(importe) as importe')))
->where('cod_prov', '=', $my_cod);
if (Input::has('nro_orden')) {
$data = $data->where('nro_orden', '=', $num);
}
$data = $data->groupBy('nro_orden')
->skip(Input::get("jtStartIndex"))
->take(Input::get("jtPageSize"))
->orderBy($search[0], $search[1])
->get();

I'm using Yii addColumnCondition and want to change the default behaviour from '=' to using LIKE and %

I'm using the addColumnCondition function as I like how it forms the queries for multiple queries. But I can't find anything in the documentation to change it's comparison operation from the simple = needle to a LIKE %needle%. There is a function that does a LIKE in addSearchCondition() but then it means to get the same query formation result, I'll have to do some for loops and merge conditions which I'd like to avoid if there is a better solution.
Here's the code
foreach($query_set as $query){
foreach($attributes as $attribute=>$v){
$attributes[$attribute] = $query;
}
$criteria->addColumnCondition($attributes, 'OR', 'AND');
}
And I'm getting the condition formed like
(business_name=:ycp0 OR payment_method=:ycp1) AND (business_name=:ycp2 OR payment_method=:ycp3)
So is there a way to configure the function to use LIKE %:ycp0% instead of the simple =:ycp0.
It seems, this feature is not provided by Yii's addColumnCondition method.
therefore i would recommend a way of overriding the method of CDbCriteria class and customize it your own way.
you need to create a new class called "AppCriteria", then place it inside protected/models
The code for the new class should look like,
i.e
class AppCriteria extends CDbCriteria {
public function addColumnCondition($columns, $columnOperator = 'AND', $operator = 'AND', $like = true) {
$params = array();
foreach ($columns as $name=>$value) {
if ($value === null)
$params[] = $name.' IS NULL';
else {
if ($like)
$params[] = $name.' LIKE %'.self::PARAM_PREFIX.self::$paramCount.'%';
else
$params[] = $name.'='.self::PARAM_PREFIX.self::$paramCount;
$this->params[self::PARAM_PREFIX.self::$paramCount++] = $value;
}
}
return $this->addCondition(implode(" $columnOperator ", $params), $operator);
}
}
Note: The 4th param of addColumnCondition, $like = true. you can set it to $like = false and allow the function to work with equal conditions. (A = B)
i.e
(business_name=:ycp0 OR payment_method=:ycp1) AND (business_name=:ycp2 OR payment_method=:ycp3)
if $like = true, it will allow you to have like condition. (A like %B%)
i.e
(business_name LIKE %:ycp0% OR payment_method LIKE %:ycp1%) AND (business_name LIKE %:ycp2% OR payment_method LIKE %:ycp3%)
Now Here's the working code,
$criteria = new AppCriteria();
foreach($query_set as $query){
foreach($attributes as $attribute=>$v){
$attributes[$attribute] = $query;
}
$criteria->addColumnCondition($attributes, 'OR', 'AND');
}

Codeigniter issue with escaping values when passing array to query with "in" in the where clause

I have the function below in my model for a codeigniter project, and the variable $id is an array and for example, contains (1,2,3). Now that i'm revisiting it, I think that i'm not actually escaping my array $id. I think I would have to change the line
$this->db->escape($id)
to
$id = $this->db->escape($id)
If I do that, then it puts single quotes around every element in the array and treats it as one long string like this: '(1,2,3)'.
Can someone confirm that I am not actually escaping my variable and either suggest a solution or let me know if this is a bug within the codeigniter framework?
function get_ratings($id)
{
$this->db->escape($id); // had to manually escape the variable since it's being used in an "in" in the where clause.
$sql = "select * from toys t
where t.toy_id in ($id)";
$query = $this->db->query($sql, $id);
if($query->num_rows() > 0)
{
return $query->result_array();
}
else
{
return false;
}
}
You may be interested in using the CI Active Record class:
Beyond simplicity, a major benefit to using the Active Record features is that it allows you to create database independent applications, since the query syntax is generated by each database adapter. It also allows for safer queries, since the values are escaped automatically by the system.
Your rewritten query would look like this (assuming $id is an array):
$this->db->where_in('toy_id', $id)->get('toys');
Aside: I will admit I am a bit confused, as it looks like $ids would be a more appropriate variable name, and the way you are using it in the query, I would assume it is a string...
If active record is not your thing, you may also find Query Bindings to be useful:
The secondary benefit of using binds is that the values are automatically escaped, producing safer queries. You don't have to remember to manually escape data; the engine does it automatically for you.
EDIT: Looking back on this later, it looks like this is what you're trying to do. In that case, try replacing:
$sql = "select * from toys t where t.toy_id in ($id)";
With:
$sql = "select * from toys t where t.toy_id in (?)";
And pass $id as the second argument to query(), but as a comma separated string (implode(',', $id) if $id is indeed an array).
Otherwise you may want to use $this->db->escape_str().
$this->db->escape_str() This function escapes the data passed to it, regardless of type.
Here is an excerpt from the source code of the mysql driver to maybe put your mind at ease.
function escape_str($str, $like = FALSE)
{
if (is_array($str))
{
foreach ($str as $key => $val)
{
$str[$key] = $this->escape_str($val, $like);
}
return $str;
}
// continued...
It loops through arrays and escapes their values.
It does indeed seem that $this->db->escape is not going to work for arrays.
$this->db->escape() This function determines the data type so that it can escape only string data.
Here is the source:
function escape($str)
{
if (is_string($str))
{
$str = "'".$this->escape_str($str)."'";
}
elseif (is_bool($str))
{
$str = ($str === FALSE) ? 0 : 1;
}
elseif (is_null($str))
{
$str = 'NULL';
}
return $str;
}
Looks like it ignores arrays.
Anyways, hope you find a solution that works for you. My vote is for Active Record.
What you want to do is escape the individual values in the array. So you can use array_map on the array first.
$id = array_map('some_escape_function', $id);
See: http://php.net/manual/en/function.array-map.php
Then you can do:
$in = join(",",$id);
Your SQL would then be:
WHERE t.toy_id in ($in)
Which gives you:
WHERE t.toy_id in ('1','2','3')
You could try something like this:
$sql = 'select * from toys t where t.toy_id in ('.
join(',',array_map(function($i) {
return $this->db->escape($i);
}, $id)).');';
*Disclaimer: I'm not where I can access my PHP/MySQL server right now, so I haven't validated this. Some modification and/or tweakage may be necessary.
Here's the solution I'm using for this, with CI 2.1.2:
1) Copy /system/database/DB.php to application/database/DB.php, and around line 123, make it look like:
...
if ( ! isset($active_record) OR $active_record == TRUE)
{
require_once(BASEPATH.'database/DB_active_rec.php');
require_once(APPPATH.'database/MY_DB_active_rec' . EXT);
if ( ! class_exists('CI_DB'))
{
eval('class CI_DB extends MY_DB_active_record { }');
}
}
...
2) Create MY_Loader.php in application/core:
class MY_Loader extends CI_Loader
{
function __construct()
{
parent::__construct();
log_message('debug', 'MY Loader Class Initialized');
}
public function database($params = '', $return = FALSE, $active_record = NULL) {
// Grab the super object
$CI = & get_instance();
// Do we even need to load the database class?
if (class_exists('CI_DB') AND $return == FALSE AND $active_record == NULL AND isset($CI->db) AND is_object($CI->db)) {
return FALSE;
}
//require_once(BASEPATH . 'database/DB.php');
require_once(APPPATH . 'database/DB' . EXT);
if ($return === TRUE) {
return DB($params, $active_record);
}
// Initialize the db variable. Needed to prevent
// reference errors with some configurations
$CI->db = '';
// Load the DB class
$CI->db = & DB($params, $active_record);
}
}
3) Create application/database/MY_DB_active_rec.php:
class MY_DB_active_record extends CI_DB_active_record {
public function __construct($params)
{
parent::__construct($params);
log_message('debug', 'MY Active Record Database Driver Class Initialized');
}
private function _array_escape(&$str)
{
$str = "'" . $this->escape_str($str) . "'";
}
function escape($str)
{
if (is_array($str))
{
array_walk($str, array($this, '_array_escape'));
return implode(',', $str);
}
elseif (is_string($str))
{
$this->_array_escape($str);
}
elseif (is_bool($str))
{
$str = ($str === FALSE) ? 0 : 1;
}
elseif (is_null($str))
{
$str = 'NULL';
}
return $str;
}
}
Then you just pass in an array of values:
$in_data = array(1, 2, 3);
$this->db->query('SELECT * FROM table WHERE id IN(?)', array($in_data));
It's not pretty, but it seems to do the trick!
Code Igniter v3 now automaticly escapes array values :
http://www.codeigniter.com/userguide3/database/queries.html
Query Bindings
Bindings enable you to simplify your query syntax by letting the system put the queries together for you. Consider the following example:
$sql = "SELECT * FROM some_table WHERE id = ? AND status = ? AND author = ?";
$this->db->query($sql, array(3, 'live', 'Rick'));
The question marks in the query are automatically replaced with the >values in the array in the second parameter of the query function.
Binding also work with arrays, which will be transformed to IN sets:
$sql = "SELECT * FROM some_table WHERE id IN ? AND status = ? AND author = ?";
$this->db->query($sql, array(array(3, 6), 'live', 'Rick'));
The resulting query will be:
SELECT * FROM some_table WHERE id IN (3,6) AND status = 'live' AND author = 'Rick'
The secondary benefit of using binds is that the values are automatically escaped, producing safer queries. You don’t have to remember to manually escape data; the engine does it automatically for you.
To bind them you can do the following:
$queryParams = [];
// to add the appropriate amount of bindings ?
$idBindings = str_replace(' ', ',', trim(str_repeat("(?) ", count($ids))));
// add each id with int validation
foreach ($ids as $id) {
if(is_int(intVal($id)) == TRUE){
array_push($queryParams, intVal($id));
}
}
// the other option commented out below is to merge without checking -
// note: sometimes values look like numeric values but are actually strings
//queryParams = array_merge($queryParams, $ids);
$sql = "select * from toys t where t.toy_id in ('. $idBindings .')";
$query = $this->db->query($sql, $queryParams );

Categories