Laravel 5: whereRaw escapes integer to string - mysql

What is the Laravel eloquent query for this:
select * from `jobs` where (
400000 between min_salary and max_salary
or
600000 between min_salary and max_salary
);
I tried the below eloquent query which encapsulates the integer to string
$min = 400000;
$max = 600000;
Job::whereRaw('
? between min_salary and max_salary
or
? between min_salary and max_salary',
[$min,$max]
)->get();
Also tried Casting and DB::Raw none of the options were worked as expected.
protected $casts = [
'min_salary' => 'integer',
'max_salary' => 'integer',
];
$min = 400000;
$max = 600000;
Job::whereRaw('
? between min_salary and max_salary
or
? between min_salary and max_salary',
[DB::Raw($min),DB::Raw($max)]
)->get();
I tried the below eloquent query works as expected but i have hard coded the query directly(Unsafe)
$min = 400000;
$max = 600000;
Job::whereRaw(
$min.' between min_salary and max_salary
or
'.$max.' between min_salary and max_salary'
)->get();

Try this:
Job::where(function($query){
$query->where('min_salary','<',400000);
$query->where('max_salary','>',400000);
})->orWhere(function($query){
$query->where('min_salary','<',600000);
$query->where('max_salary','>',600000);
})->get();

Related

Convert MYSQL RAW included hash(encrypt) query to Laravel eloquent query

This is the raw query
"SELECT * FROM re_customer
WHERE LOWER(email) = '" . $input['email'] . "'
AND
(password = SHA1(CONCAT(salt, SHA1(CONCAT(salt, SHA1('" . $input['password'] . "')))))
OR password = '" . md5( $input['password'] ) . "')
AND status = '1'";
I tried
$customer = DB::connection( 'oc' )
->table( Customer::$customerTable )
->where( DB::raw( 'LOWER(email)' ), '=', mb_strtolower( $input['email'] ) )
->whereRaw( "(password = SHA1(CONCAT(salt, SHA1(CONCAT(salt, SHA1('?'))))) OR password = '?')", [
$input['password'],
md5($input['password'])
] )
->where( 'status', 1 )
->first();
But not working for me (both query results are not same). Can you please address me that what wrong on this query.
I solved the query conversion.
$customer = DB::connection( 'oc' )
->table( Customer::$customerTable )
->where( DB::raw( 'LOWER(email)' ), '=', mb_strtolower( $input['email'] ) )
->where( 'status', 1 )
->where( function ( $query ) use ( $input ) {
$query->whereRaw( "password = SHA1(CONCAT(salt, SHA1(CONCAT(salt, SHA1(?)))))", $input['password'] )
->orWhere( 'password', '=', md5( $input['password'] ) );
} )
->first();
this is working like a charm

How to make Laravel eloquent request with 1 filter on many fields

In my Laravel 5.7/ mysql app I make request on a form with 10 filter inputs,
one of them ($filter_search) if non empty must be compared with all fields(string, number,
date, ref to fields of other tables) in resulting listing.
I made scope on this table fields:
public function scopeGetBySearch($query, $search = null)
{
if (empty($search)) {
return $query;
}
$tb= with(new StorageSpace)->getTable();
return $query->where(
$tb.'.number', $search
) ->orWhere(function ($query) use ($search, $tb) {
$query->where( $tb.".notes", 'like', '%'.$search.'%' )
->orWhere($tb.".selling_range", $search)
->orWhere($tb.".actual_storage_rent", $search)
->orWhere($tb.".insurance_vat", $search)
// ->havingRaw("job_ref_no", $search)
})
But I have a problem how can I set filter on job_ref_no field from other table :
$storageSpacesCollection = StorageSpace
::getByStatus($filter_status)
->getById($relatedStorageSpacesArray)
->getByLocationId($filter_location_id, 'warehouses')
->getByCapacityCategoryId($filter_capacity_category_id, 'storage_capacities')
->getByLevel($filter_level)
->getByNumber($filter_number, true)
->orderBy('storage_spaces.id', 'asc')
->getByStorageCapacityId($filter_storage_capacity_id)
->getByClientId($filter_client_id)
->getByColorId($filter_color_id)
->getBySearch($filter_search)
// ->havingRaw("job_ref_no = " . $filter_search)
->leftJoin( 'storage_capacities', 'storage_capacities.id', '=', 'storage_spaces.storage_capacity_id' )
->leftJoin( 'warehouses', 'warehouses.id', '=', 'storage_spaces.warehouse_id' )
->leftJoin( 'clients', 'clients.id', '=', 'storage_spaces.client_id')
->select(
"storage_spaces.*",
\DB::raw( "CONCAT(storage_capacities.count, ' ', storage_capacities.sqft ) as storage_capacity_name" ),
\DB::raw("( SELECT check_ins.job_ref_no FROM check_ins WHERE // I got job_ref_no field in subquesry check_ins.storage_space_id=storage_spaces.id ORDER BY check_ins.id ASC limit 1 ) AS job_ref_no"),
"warehouses.name as warehouse_name",
"clients.full_name as client_full_name")
->get();
havingRaw does not work both in the scope and in the request above if to uncomment it.
I tried to use addSelect, like:
But with request :
$storageSpacesCollection = StorageSpace
::getByStatus($filter_status)
->whereRaw('storage_spaces.id <= 8') // DEBUGGING
->getById($relatedStorageSpacesArray)
->getByLocationId($filter_location_id, 'warehouses')
->getByCapacityCategoryId($filter_capacity_category_id, 'storage_capacities')
->getByLevel($filter_level)
->getByNumber($filter_number, true)
->orderBy('storage_spaces.id', 'asc')
->getByStorageCapacityId($filter_storage_capacity_id)
->getByClientId($filter_client_id)
->getByColorId($filter_color_id)
->getBySearch($filter_search)
// ->havingRaw("job_ref_no = " . $filter_search)
->leftJoin( 'storage_capacities', 'storage_capacities.id', '=', 'storage_spaces.storage_capacity_id' )
->leftJoin( 'warehouses', 'warehouses.id', '=', 'storage_spaces.warehouse_id' )
->leftJoin( 'clients', 'clients.id', '=', 'storage_spaces.client_id')
->select(
"storage_spaces.*",
\DB::raw( "CONCAT(storage_capacities.count, ' ', storage_capacities.sqft ) as storage_capacity_name" ),
"warehouses.name as warehouse_name",
"clients.full_name as client_full_name")
->addSelect([
'job_ref_no' => CheckIn::selectRaw('job_ref_no')->whereColumn('check_ins.storage_space_id', 'storage_spaces.id'),
])
->get();
But I got an error:
local.ERROR: stripos() expects parameter 1 to be string, object given {"userId":11,"email":"nilovsergey#yahoo.com","exception":"[object] (ErrorException(code: 0): stripos() expects parameter 1 to be string, object given at /mnt/_work_sdb8/wwwroot/lar/the-box-booking/vendor/laravel/framework/src/Illuminate/Database/Query/Grammars/Grammar.php:1031)
[stacktrace]
#0 [internal function]: Illuminate\\Foundation\\Bootstrap\\HandleExceptions->handleError(2, 'stripos() expec...', '/mnt/_work_sdb8...', 1031, Array)
#1 /mnt/_work_sdb8/wwwroot/lar/the-box-booking/vendor/laravel/framework/src/Illuminate/Database/Query/Grammars/Grammar.php(1031): stripos(Object(Illuminate\\Database\\Eloquent\\Builder), ' as ')
#2 [internal function]: Illuminate\\Database\\Query\\Grammars\\Grammar->wrap(Object(Illuminate\\Database\\Eloquent\\Builder))
Which way is valid ?
MODIFIED BLOCK:
I try to create sql I need manually.
With Laravel request :
$storageSpacesCollection = StorageSpace
::getByStatus($filter_status)
->whereRaw('storage_spaces.id <= 8') // DEBUGGING
->getById($relatedStorageSpacesArray)
->getByLocationId($filter_location_id, 'warehouses')
->getByCapacityCategoryId($filter_capacity_category_id, 'storage_capacities')
->getByLevel($filter_level)
->getByNumber($filter_number, true)
->orderBy('storage_spaces.id', 'asc')
->getByStorageCapacityId($filter_storage_capacity_id)
->getByClientId($filter_client_id)
->getByColorId($filter_color_id)
->getBySearch($filter_search)
->leftJoin( 'storage_capacities', 'storage_capacities.id', '=', 'storage_spaces.storage_capacity_id' )
->leftJoin( 'warehouses', 'warehouses.id', '=', 'storage_spaces.warehouse_id' )
->leftJoin( 'clients', 'clients.id', '=', 'storage_spaces.client_id')
->select(
"storage_spaces.*",
\DB::raw( "CONCAT(storage_capacities.count, ' ', storage_capacities.sqft ) as storage_capacity_name" ),
\DB::raw("( SELECT check_ins.job_ref_no FROM check_ins WHERE check_ins.storage_space_id=storage_spaces.id ORDER BY check_ins.id ASC limit 1 ) AS job_ref_no"),
"warehouses.name as warehouse_name",
"clients.full_name as client_full_name")
->get();
And tracing I see sql statement with job_ref_no column :
SELECT `storage_spaces`.*, CONCAT(storage_capacities.count, ' ', storage_capacities.sqft ) AS storage_capacity_name, ( SELECT check_ins.job_ref_no
FROM check_ins
WHERE check_ins.storage_space_id=storage_spaces.id
ORDER BY check_ins.id ASC limit 1 ) AS job_ref_no, `warehouses`.`name` AS `warehouse_name`, `clients`.`full_name` AS `client_full_name`
FROM `storage_spaces`
LEFT JOIN `storage_capacities` on `storage_capacities`.`id` = `storage_spaces`.`storage_capacity_id`
LEFT JOIN `warehouses` on `warehouses`.`id` = `storage_spaces`.`warehouse_id`
LEFT JOIN `clients` on `clients`.`id` = `storage_spaces`.`client_id`
WHERE storage_spaces.id <= 8 AND (`storage_spaces`.`number` = '999' OR
(`storage_spaces`.`notes` like '%999%' OR
`storage_spaces`.`selling_range` = '999' OR
`storage_spaces`.`actual_storage_rent` = '999' OR
`storage_spaces`.`insurance_vat` = '999') )
ORDER BY `storage_spaces`.`id` asc
I want to set filter on job_ref_no column manually :
SELECT `storage_spaces`.*, CONCAT(storage_capacities.count, ' ', storage_capacities.sqft ) AS storage_capacity_name, ( SELECT check_ins.job_ref_no
FROM check_ins
WHERE check_ins.storage_space_id=storage_spaces.id
ORDER BY check_ins.id ASC limit 1 ) AS job_ref_no, `warehouses`.`name` AS `warehouse_name`, `clients`.`full_name` AS `client_full_name`
FROM `storage_spaces`
LEFT JOIN `storage_capacities` on `storage_capacities`.`id` = `storage_spaces`.`storage_capacity_id`
LEFT JOIN `warehouses` on `warehouses`.`id` = `storage_spaces`.`warehouse_id`
LEFT JOIN `clients` on `clients`.`id` = `storage_spaces`.`client_id`
WHERE storage_spaces.id <= 8 AND (`storage_spaces`.`number` = '999' OR
(`storage_spaces`.`notes` like '%999%' OR
`storage_spaces`.`selling_range` = '999' OR
`storage_spaces`.`actual_storage_rent` = '999' OR
`storage_spaces`.`insurance_vat` = '999' OR
job_ref_no = '999' ) // I added this line #
)
ORDER BY `storage_spaces`.`id` asc
But I got error :
Error in query (1054): Unknown column 'job_ref_no' in 'where clause'
Which is valid way in raw sql and how it can be implemented with eloquent ?
MODIFIED BLOCK # 2:
I try to make with join:
$storageSpacesCollection = StorageSpace
::getByStatus($filter_status)
->getById($relatedStorageSpacesArray)
->getByLocationId($filter_location_id, 'warehouses')
->getByCapacityCategoryId($filter_capacity_category_id, 'storage_capacities')
->getByLevel($filter_level)
->getByNumber($filter_number, true)
->orderBy('storage_spaces.id', 'asc')
->getByStorageCapacityId($filter_storage_capacity_id)
->getByClientId($filter_client_id)
->getByColorId($filter_color_id)
->getBySearch($filter_search)
->leftJoin( 'storage_capacities', 'storage_capacities.id', '=', 'storage_spaces.storage_capacity_id' )
->leftJoin( 'warehouses', 'warehouses.id', '=', 'storage_spaces.warehouse_id' )
->leftJoin( 'clients', 'clients.id', '=', 'storage_spaces.client_id')
->leftJoin( 'check_ins', 'check_ins.storage_space_id', '=', 'storage_spaces.id') // I ADDED THIS LINE
->select(
"storage_spaces.*",
\DB::raw( "CONCAT(storage_capacities.count, ' ', storage_capacities.sqft ) as storage_capacity_name" ),
// \DB::raw("( SELECT check_ins.job_ref_no FROM check_ins WHERE check_ins.storage_space_id=storage_spaces.id ORDER BY check_ins.id ASC limit 1 ) AS job_ref_no"),
"warehouses.name as warehouse_name",
"check_ins.job_ref_no as job_ref_no",
"clients.full_name as client_full_name")
->distinct()
->get();
and I have a sql :
SELECT distinct `storage_spaces`.*, CONCAT(storage_capacities.count, ' ', storage_capacities.sqft ) AS storage_capacity_name,
`warehouses`.`name` AS `warehouse_name`, `check_ins`.`job_ref_no` AS `job_ref_no`, `clients`.`full_name` AS `client_full_name`
FROM `storage_spaces`
LEFT JOIN `storage_capacities` on `storage_capacities`.`id` = `storage_spaces`.`storage_capacity_id`
LEFT JOIN `warehouses` on `warehouses`.`id` = `storage_spaces`.`warehouse_id`
LEFT JOIN `clients` on `clients`.`id` = `storage_spaces`.`client_id`
LEFT JOIN `check_ins` on `check_ins`.`storage_space_id` = `storage_spaces`.`id`
WHERE ( `storage_spaces`.`number` = 'S1-102' OR (`storage_spaces`.`notes` like '%S1-102%' OR `storage_spaces`.`selling_range` = 'S1-102' OR
`storage_spaces`.`actual_storage_rent` = 'S1-102' OR `storage_spaces`.`insurance_vat` = 'S1-102' OR `job_ref_no` = 'S1-102') )
ORDER BY `storage_spaces`.`id` asc
I have have different results
I need to get only last row from check_ins, that is why in my request I have limit 1:
\DB::raw("( SELECT check_ins.job_ref_no FROM check_ins WHERE check_ins.storage_space_id=storage_spaces.id ORDER BY check_ins.id ASC limit 1 ) AS job_ref_no"),
that is why have have several rows of as storage_spaces as all check_ins ae joined
2) I have all rows from storage_spaces and I do not see why
I tried to change leftJoin with Join /rightJoin but it did not help...
Thanks!
You need to set the join into the main query instead of select query because in select query it's not a actual column which you set into the where condition.
I found a decision addin in scope :
->orWhereRaw("(SELECT check_ins.job_ref_no FROM check_ins WHERE
check_ins.storage_space_id=storage_spaces.id ORDER BY check_ins.id ASC LIMIT 1) = '".$search."'")
and that works for me.

With eloquent to make search by 2 fields

Is there a way in Laravel 5.6/MySQL 5.7 with eloquent to make search with condition:
need to check if sale_price is set then search in sale_price else in retail_price
Have I use condition like :
\DB::raw( ‘ isNull( sale_price, retail_price ) > 10’ )
and how to modify the search above if both sale_price and retail_price are null?
Or there is better solution ?
MODIFIED :
I implemented it as scope :
public function scopeGetByDoublePrice($query, $price_from= null, $price_to= null)
{
// need to check if sale_price is set then search in sale_price else in retail_price
if ( empty($price_from) and empty($price_to) ) return $query;
if ( !empty($price_from) ) { // $price_from is set
$query->whereNotNull('sale_price')->where('sale_price', '>=', $price_from);
$query->orWhere('retail_price', '>=', $price_from);
} // if (!empty($price_from) ) { // $price_from is set
if ( !empty($price_to) ) { // $price_to is set
$query->whereNotNull('sale_price')->where('sale_price', '<=', $price_to);
$query->orWhere('retail_price', '<=', $price_to);
} // if (!empty($price_to) ) { // $price_to is set
return $query;
}
but setting 2 values 31 and 33 I have different result I expected :
https://imgur.com/a/AYTJAJn
I expected only the first row would be in results set!
In sql-editor I set "()" manually, as :
SELECT sale_price, retail_price
FROM articles
LEFT JOIN brands AS b on b.id = articles.brand_id
LEFT JOIN article_inventories AS ai on ai.article_id = articles.id
WHERE ( ( sale_price is not null AND sale_price >= '31' OR retail_price >= '31' ) AND
( sale_price is not null AND sale_price <= '33' OR retail_price <= '33') )
and it works as I need. If there is a way to set "()" in my scope?
Use to following constraint:
$price = 10;
$builder->where(function($query) use ($price) {
$query->whereNotNull('sale_price')->where('sale_price', '>', $price);
$query->orWhere('retail_price', '>', $price);
});
This will add the following to your query:
AND (sale_price IS NOT NULL and sale_price > 10 OR retail_price > 10)
What's important, you'll find those extra constraints wrapped in parenthesis which will let you avoid the situation, when other constraints are ignored because of the OR.

SQL, MySQL and php

I have 2 tables
CREATE TABLE `employee` (
`Employee_ID` smallint(4) NOT NULL AUTO_INCREMENT,
`Name` varchar(25) NOT NULL DEFAULT '',
PRIMARY KEY (`Employee_ID`)
) ;
--- -----------------
CREATE TABLE `attendance` (
`Attendance_ID` int(9) NOT NULL AUTO_INCREMENT,
`Employee_ID` smallint(3) NOT NULL,
`Date` date NOT NULL,
`Attendance` char(1) NOT NULL DEFAULT '',
PRIMARY KEY (`Attendance_ID`)
) ;
For Attendance field it's a "P" when the employee is present "L" when late and "A" when absent.
I want to make a query that crosses the dates with employees names and show atendance.
Something similar to:
Attendance 07/02/2015 14/02/2015 21/02/2015 ...
Diane P P L
Robert A P P
Karine P P A
...
I didn't post any query because actually I failed many times to figure out how to
Thanks for help
I do not know how to get dynamic columns in MySQL. This is how I would do it:
SELECT attd.Date, attd.Attendance, empl.Name FROM attendance attd, employee empl WHERE empl.Employee_ID = attd.Employee_ID
That will give result:
Then I would use server side code (example PHP):
<?php
$attendance = array();
foreach ($query as $row) {
if (isset($attendance[$row['Name']])) {
$attendance[$row['Name']][$row['Date']] = $row['Attendance'];
} else {
$attendance[$row['Name']] = array($row['Date'] => $row['Attendance']);
}
}
print_r($attendance);
/* Array(
"Diane" => Array("2015-02-07" => 'P', "2015-02-14" => 'P'),
"Robert" => Array("2015-02-07" => 'P', "2015-02-14" => 'P'),
"Karine" => Array("2015-02-07" => 'L', "2015-02-14" => 'L')
)
*/
select
a.Employee_ID,
min(e.Name) as Employee_Name,
case when a.Date = dateadd(:basedate, interval 7*0 day) then min(a.Attendance) end as d0 /* d for data? */
case when a.Date = dateadd(:basedate, interval 7*1 day) then min(a.Attendance) end as d1
case when a.Date = dateadd(:basedate, interval 7*2 day) then min(a.Attendance) end as d2
case when a.Date = dateadd(:basedate, interval 7*3 day) then min(a.Attendance) end as d3
dateadd(:basedate, interval 7*0 day) as week0
dateadd(:basedate, interval 7*1 day) as week1
dateadd(:basedate, interval 7*2 day) as week2
dateadd(:basedate, interval 7*3 day) as week3
from
attendance as a inner join employee as e ...
where a.Date between :basedate and dateadd(:basedate, interval 7*3 day)
group by
a.Employee_ID
You could pass in a :basedate parameter and offset a fixed number of weeks from that point. (I don't know what's the parameter convention for PHP and MySQL.) This will work if you can fix your results to a set number of weeks.
Pick out the dates from the first row to build the header and then build the rest of the table as you normally would. Yes there's some redundant data but there's no easy way to alias the columns with the dates themselves.
Following query you can use to gain the shown output along with a bit of PHP application
SELECT E.Name, A.Date, A.Attendance
FROM attendance AS A
INNER JOIN employee AS E
ON A.Employee_ID = E.Employee_ID
ORDER BY A.Date, E.Name
For implementing this in PHP
<?php
if (!$link = mysql_connect('< mysql_host >', '< mysql_user >', '< mysql_password >')) {
echo 'Could not connect to mysql';
exit;
}
if (!mysql_select_db('< mysql_dbname >', $link)) {
echo 'Could not select database';
exit;
}
$sql = 'SELECT E.Name, A.Date, A.Attendance '.
'FROM attendance AS A '.
'INNER JOIN employee AS E '.
'ON A.Employee_ID = E.Employee_ID '.
'ORDER BY E.Name, A.Date';
$result = mysql_query($sql, $link);
if (!$result) {
echo "DB Error, could not query the database\n";
echo 'MySQL Error: ' . mysql_error();
exit;
}
$name = '';
$resultArray = array();
$attendance = array();
foreach ($query as $row) {
if (isset($attendance[$row['Name']])) {
array_push($attendance[$row['Name']], $row['Date'] => $row['Attendance']);
} else {
$attendance[$row['Name']] = array($row['Date'] => $row['Attendance']);
}
}
print_r($attendance);
mysql_free_result($result);
?>
You would get an a associative array like this
Array(
"Diane" => Array("2015-02-07" => 'P', "2015-02-14" => 'P'),
"Robert" => Array("2015-02-07" => 'P', "2015-02-14" => 'P'),
"Karine" => Array("2015-02-07" => 'L', "2015-02-14" => 'L')
)
Now you can use this associative array to in a loop to render these elements in the Web page
Hope this helps...

Laravel Multiple WHERE() Operator Precedence

I have the following query written using Eloquent:
Contact::select(DB::raw("DATE_FORMAT(DATE(`created_at`),'%b %d') as date"))
->addSelect(DB::raw("`created_at`"))
->addSelect(DB::raw("COUNT(*) as `count`"))
->where('created_at', '>', $date)
->ofType($type)
->groupBy('date')
->orderBy('created_at', 'ASC')
->lists('count', 'date');
You can see it uses a query scope method ofType() Here is that method, it just adds a bunch of extra where clauses to the query:
return $query->where('list_name', '=', 'Apples')
->orWhere('list_name', '=', 'Oranges')
->orWhere('list_name', '=', 'Pears')
->orWhere('list_name', '=', 'Plums')
->orWhere('list_name', '=', 'Blueberries');
Ultimately this results in the following real SQL query:
SELECT DATE_FORMAT(DATE(`created_at`),'%b %d') as date,`created_at`, COUNT(*) as `count`
FROM `contacts`
WHERE `created_at` > '2014-10-02 00:00:00'
AND `list_name` = 'Apples'
OR `list_name` = 'Oranges'
OR `list_name` = 'Pears'
OR `list_name` = 'Plums'
OR `list_name` = 'Blueberries'
GROUP BY `date`
ORDER BY `created_at` ASC
The problem is, the WHERE created_at > '2014-10-02 00:00:00' clause is being missed when the OR clauses kick in. Due to operator precendence. I need to wrap all the clauses after the first AND in parentheses, like so:
SELECT DATE_FORMAT(DATE(`created_at`),'%b %d') as date,`created_at`, COUNT(*) as `count`
FROM `contacts`
WHERE `created_at` > '2014-10-02 00:00:00'
AND
(`list_name` = 'Apples'
OR `list_name` = 'Oranges'
OR `list_name` = 'Pears'
OR `list_name` = 'Plums'
OR `list_name` = 'Blueberries')
GROUP BY `date`
ORDER BY `created_at` ASC
So, my question is, how would I achieve this using the eloquent query builder. Thank you.
Thanks to mOrsa I've figured it out, by changing my query scope method to take advantage of advanced where:
return $query->where(function($query){
$query->orWhere('list_name', '=', 'Apples')
->orWhere('list_name', '=', 'Oranges')
->orWhere('list_name', '=', 'Pears')
->orWhere('list_name', '=', 'Plums')
->orWhere('list_name', '=', 'Blueberries');
});
I get the desired SQL.