Querying eloquent model with the range of alphabets - php

I am trying to select the users who are listed in the given range of their first name initials.
For example, if the name of the person starts with letter C then the user will select the range, lets say, A-E. So for that, he will click on range A-E and that range will display all the users whose name starts with either A, B, C, D and E.
In order to do that I am trying to query the laravel eloquent, but I cannot find the desired solution. On bit of research, I found this link that provides the solution, but it didn't provided me what I am looking for.
The query that I did is:
public function getAllUsers(Request $request)
{
$users = User::where('account_type', 'beneficiary')->latest()->get();
if($request->range !== null) {
$users = User::where('account_type', 'beneficiary')->where('full_name', "REGEXP '^".$request->range.".*$'")->latest()->get();
dd($users); // returned an empty Collection object
}
return view('pages.beneficiaries', compact('users'));
}
Bottom Line: Fetch the users whose name starts within the given range that the user will select.
Any help is highly appreciated. Thanks.
P.S: I know this might be quite simple to solve, but I am failing in it.
UPDATE 1: Solution
With the help of #Armen, I was able to solve the issue. The query that I passed to the eloquent model is the following:
// Notice the curly braces and also the removal of single quotes
$users = User::where('account_type', 'beneficiary')
->where('full_name', 'REGEXP', "^[{$request->range}].*$")
->get();

According to your found example (mentioned in issue description) value of REGEXP should be in scopes [] like this '^[A-E].*$' so change your second where to where('full_name', "REGEXP '^[".$request->range."].*$'") and also take into account Upper and Lowercase lettera-e is not same as A-E
UPDATE
as we founded out with #user3514160 the where condition should look like this
->where('full_name', 'REGEXP', "^[{$request->range}].*$")

Related

Get distinct columns and return filtered result

Currently i'm looking to display a few holiday categories on the home-page of a website.
In-order to create these categories, I will need to pull a unique set of countries out of the existing holidays.
For example; one holiday may go to Mexico, another 3 may go to Thailand, another may go India.
Notice the 3 holidays going to Thailand, with a basic pull they are all returned; but I only need one of them in-order to create the category. So they must be distinct.
Here's my current code.
public function getHolidays($mode = "all", $limit = 1000){
$holidays = Holiday::whereHas('dates', function(Builder $q){
$q->where('start_sydney', '>=', date('Y-m-d'));
});
switch($mode){
case "home":
$holidays = $holidays->whereHas('info', function($q) {
});
break;
}
$holidays = $holidays
->with('dates')
->with('pricing')
->with('info')
->with('images')
->with('images.image_info')
->limit($limit)->get();
return json_encode($holidays);
}
In the home case, I tried to have $q->distinct('country') but it didn't work.
The country column in the 'info' model needs to be used but i'm not sure how to make it unique.
Could someone assist me in returning a unique set of records based on their 'country' column in the info model?
Try doing it with join and using groupby
switch($mode){
case "home":
$holidays = $holidays->join('info', 'info.id', '=', 'holidays.id')->groupBy('info.country');
break;
}
One thing that is often mistaken with Laravel query builder / eloquent is distinct. You can't pass anything to distinct because it doesn't take any parameters.
That said, I do not know exactly how you have your models set up, but assuming they are somewhat standard, you could try essentially what you are attempting, but in reverse:
Info::select('country')->has('holiday')->distinct()->pluck('country');
Which should return what you are after.
I used pluck here because it would return a nice, simple, ready to use Collection of country names for you to use on the front end.
There are a few ways to do what you want, this is one of them. Hope it helps!
More about distinct from the docs:
The distinct method allows you to force the query to return distinct
results:
$users = DB::table('users')->distinct()->get();

CONCAT columns with Laravel 5 eloquent

Consider me as laravel beginner
The goal is: I have two colums, now I need the id to be prefixed with the component name of same row in the table.
For Example (Working)... I have Mysql like
SELECT CONCAT(components.name," ", components.id) AS ID
FROM `components`
And output is
ID
|TestComp 40 |
-------------
|component 41 |
-------------
|test 42 |
I need the same in laravel eloquent way, as here Component is Model name. So i tried something like
$comp=Component::select("CONCAT('name','id') AS ID")->get()
but it doesn't work.
I think because the syntax is wrong.
Kindly help me with the correct syntax. Using laravel Models.
Note: I made the above query, referring this as which available on internet.
User::select(DB::raw('CONCAT(last_name, first_name) AS full_name'))
You need to wrap your query in DB::raw:
$comp = Component::select(DB::raw("CONCAT('name','id') AS ID"))->get()
Also, note because you are doing your query like this, your model might behave differently, because this select removes all other fields from the select statement.
So you can't read the other fields from your model without a new query. So ONLY use this for READING data and not MODIFYING data.
Also, to make it in a nice list, I suggest you modify your query to:
$comp = Component::select(DB::raw("CONCAT('name','id') AS display_name"),'id')->get()->pluck('display_name','id');
// dump output to see how it looks.
dd($comp);// array key should be the arrray index, the value the concatted value.
I came to this post for answers myself. The only problem for me is that the answer didn't really work for my situation. I have numerous table relationships setup and I needed one of the child objects to have a concatenated field. The DB::raw solution was too messy for me. I kept searching and found the answer I needed and feel it's an easier solution.
Instead of DB::raw, I would suggest trying an Eloquent Accessor. Accessors allow you to retrieve model attributes AND to create new ones that are not created by the original model.
For instance, let's say I have a basic USER_PROFILE table. It contains id, first_name, last_name. I have the need to CONCAT the two name attributes to return their user's full name. In the USER_PROFILE Model I created php artisan make:model UserProfile, I would place the following:
class UserProfile extends Model
{
/**
* Get the user's full concatenated name.
* -- Must postfix the word 'Attribute' to the function name
*
* #return string
*/
public function getFullnameAttribute()
{
return "{$this->first_name} {$this->last_name}";
}
}
From here, when I make any eloquent calls, I now have access to that additional attribute accessor.
| id | first_name | last_name |
-------------------------------
| 1 | John | Doe |
$user = App\UserProfile::first();
$user->first_name; /** John **/
$user->fullname; /** John Doe **/
I will say that I did run into one issue though. That was trying to create a modified attribute with the same name, like in your example (id, ID). I can modify the id value itself, but because I declared the same name, it appears to only allow access to that field value and no other field.
Others have said they can do it, but I was unable to solve this questions EXACT problem.
I working on posgresql and mysql:
DB::raw('CONCAT(member.last_name, \' \', member.first_name) as full_name')
$text = "other";
$limit = 100
public function get_data($text, $limit)
{
$result = $this->select('titulo', 'codigo', DB::Raw("CONCAT(codigo, ' ', titulo_long) AS text_search"))
->where('tipo', '=', 2)
->having('text_search', 'LIKE', "%$text%")
->limit($limit)
->get();
return $result;
}
}
Here is the example of columns concatenation in Laravel.
I need to search the user by name and I have three columns for the user name (name_first, name_middle, name_last), so I have created a scope in Laravel UserModel which takes $query and user name as the second parameter.
public function scopeFindUserByName($query,$name) {
// Concat the name columns and then apply search query on full name
$query->where(DB::raw(
// REPLACE will remove the double white space with single (As defined)
"REPLACE(
/* CONCAT will concat the columns with defined separator */
CONCAT(
/* COALESCE operator will handle NUll values as defined value. */
COALESCE(name_first,''),' ',
COALESCE(name_middle,''),' ',
COALESCE(name_last,'')
),
' ',' ')"
),
'like', '%' . $name . '%');
}
and you can use this scope anywhere you need to search user by his name, like
UserModel::findUserByName("Max Begueny");
OR
$query = UserModel::query();
$query->findUserByName("Max Begueny");
To check the result of this SQL query just go through from this post.
https://stackoverflow.com/a/62296860/11834856
this code should work:
User::select(\DB::raw('CONCAT(last_name, first_name) AS full_name)')
Scrubbing the Data: Before using Laravel and now, when developing in other languages, I would use CONCAT() on a regular basis. The answers here work to a degree but there still isn't an elegant way to use CONCAT() in Laravel/Eloquent/Query Builder
that I have found.
However, I have found that concatenating the cols AFTER returning the results works well for me and is usually very fast - Scrubbing the data - ( unless you have a huge result which should probably be "chunked" anyway for performance purposes ).
foreach($resultsArray AS $row){
$row['fullname'] = trim($row['firstname']).' '.trim($row['lastname']);
}
This is a tradeoff of course but, personally, I find it to be much more manageable and doesn't limit my use of Eloquent as intended as well as the Query Builder. ( the above is pseudo code - not tested so tweak as needed )
There are other workarounds as well that don't mess with Eloquent/Query Builder functionality such as creating a concatenated col in the table, in this case full_name - save the full name when the record is inserted/updated. This is not uncommon.

Laravel 5 eloquent whereIn

Hope anybody can help me, I need to search for items that have category id = x in the database
Example table items
id,cats,name etc...
cats = '1,19' or maybe just '19' or maybe '1,9'
So for this example I need a to search for items that have cats with 9
I tried this but when I search for 9 it also shows 19
$items = Items::where(function($query)use($cat) {
$query->where('cats', 'like', '%,'.$cat->id.'%');
$query->orWhere('cats', 'like', '%'.$cat->id.',%');
$query->orWhere('cats', 'like', '%'.$cat->id.'%');
})->orderBy('updated_at', 'DSC')->get();
I also tried something
$items = Items::whereIn(explode(',', 'cats'), $cat->id)->get();
but it doesn't work
Appreciate any help to find the easiest and shorts way of doing this, regards
It's quite hard to understand what you want to achieve but I'll try. First of all as #particus mentioned the best way is to create pivot table when you don't need to worry about such things.
But the solution if you have list of ids in a columns separated by coma is not storing values like
1,2,3
but always adding , at the beginning and at the end, so it should be in this case:
,1,2,3,
This way, if you have in your table ,19,2,3, and you want to search for value 9, you should use look for ,9, string, for example:
$id = 9;
$items = Items::where('column', LIKE '%,'.$id.',%')->get();
Now for above string no record will be found, but if you have ,9,2,3, or just ,9, the desired record will be found.
Assuming you're using MySQL, you can use the FIND_IN_SET function.
$items = Items::whereRaw("FIND_IN_SET(".$cat->id.", cats)")->orderBy('updated_at', 'DESC')->get();
Please note, this will not use any indexes defined on the cats column. Storing array like data in a field is usually a big red flag. You would benefit by normalizing this out now, rather than trying to work around the current design.

Laravel: How to get last N entries from DB

I have table of dogs in my DB and I want to retrieve N latest added dogs.
Only way that I found is something like this:
Dogs:all()->where(time, <=, another_time);
Is there another way how to do it? For example something like this Dogs:latest(5);
Thank you very much for any help :)
You may try something like this:
$dogs = Dogs::orderBy('id', 'desc')->take(5)->get();
Use orderBy with Descending order and take the first n numbers of records.
Update (Since the latest method has been added):
$dogs = Dogs::latest()->take(5)->get();
My solution for cleanliness is:
Dogs::latest()->take(5)->get();
It's the same as other answers, just with using built-in methods to handle common practices.
Dogs::orderBy('created_at','desc')->take(5)->get();
You can pass a negative integer n to take the last n elements.
Dogs::all()->take(-5)
This is good because you don't use orderBy which is bad when you have a big table.
You may also try like this:
$recentPost = Article::orderBy('id', 'desc')->limit(5)->get();
It's working fine for me in Laravel 5.6
I use it this way, as I find it cleaner:
$covidUpdate = COVIDUpdate::latest()->take(25)->get();
Ive come up with a solution that helps me achieve the same result using the array_slice() method. In my code I did array_slice( PickupResults::where('playerID', $this->getPlayerID())->get()->toArray(), -5 ); with -5 I wanted the last 5 results of the query.
The Alpha's solution is very elegant, however sometimes you need to re-sort (ascending order) the results in the database using SQL (to avoid in-memory sorting at the collection level), and an SQL subquery is a good way to achieve this.
It would be nice if Laravel was smart enough to recognise we want to create a subquery if we use the following ideal code...
$dogs = Dogs::orderByDesc('id')->take(5)->orderBy('id')->get();
...but this gets compiled to a single SQL query with conflicting ORDER BY clauses instead of the subquery that is required in this situation.
Creating a subquery in Laravel is unfortunately not simply as easy as the following pseudo-code that would be really nice to use...
$dogs = DB::subQuery(
Dogs::orderByDesc('id')->take(5)
)->orderBy('id');
...but the same result can be achieved using the following code:
$dogs = DB::table('id')->select('*')->fromSub(
Dogs::orderByDesc('id')->take(5)->toBase(),
'sq'
)->orderBy('id');
This generates the required SELECT * FROM (...) AS sq ... sql subquery construct, and the code is reasonably clean in terms of readability.)
Take particular note of the use of the ->toBase() function - which is required because fromSub() doesn't like to work with Eloquent model Eloquent\Builder instances, but seems to require a Query\Builder instance). (See: https://github.com/laravel/framework/issues/35631)
I hope this helps someone else, since I just spent a couple of hours researching how to achieve this myself. (I had a complex SQL query builder expression that needed to be limited to the last few rows in certain situations).
For getting last entry from DB
$variable= Model::orderBy('id', 'DESC')->limit(1)->get();
Imagine a situation where you want to get the latest record of data from the request header that was just inserted into the database:
$noOfFilesUploaded = count( $request->pic );// e.g 4
$model = new Model;
$model->latest()->take($noOfFilesUploaded);
This way your take() helper function gets the number of array data that was just sent via the request.
You can get only ids like so:
$model->latest()->take($noOfFilesUploaded)->puck('id')
use DB;
$dogs = DB::select(DB::raw("SELECT * FROM (SELECT * FROM dogs ORDER BY id DESC LIMIT 10) Var1 ORDER BY id ASC"));
Dogs::latest()->take(1)->first();
this code return the latest record in the collection
Can use this latest():
$dogs = Dogs::latest()->take(5)->get();

How can i query mysql with just numbers when a user may type in a suffix?

I have a function below which works perfectly, but now the client came back and asked that the number only be taken to do the search because most of his clients won't type in the suffix "h" or whatever it may be as per my example below:
38039 or 38039h
However he also said he only has one group of product codes which begin with "T" so they could be typing in "T760" in which case we would need the prefix.
My code below does a search on the exact product currently, can anyone help me work in these examples?
<?php
//Find Stock Value
function checkstock($prodCode) {
$prodCode = strtoupper($prodCode);
require '../../../../config.php';
$dbh = new PDO(DB_DSN, DB_USER, DB_PASS);
$sql = "SELECT * FROM isproducts WHERE prodCode = '".
$prodCode."' AND AllowSalesOrder = '1'";
$stmt = $dbh->query($sql);
$obj = $stmt->fetch(PDO::FETCH_OBJ);
$count = $stmt->rowCount();
echo ($count == 1 ?
ROUND($obj->FreeStockQuantity, 0) : 'Invalid product code '.$prodCode.'');
}
//Call Stock Function
checkstock($_POST['productcode']);
?>
Change the query to like below ?
SELECT * FROM isproducts
WHERE
(
prodCode='{$prodCode}' // for product with prefix or suffix
OR prodCode LIKE '{$prodCode}%' // without suffix
OR prodCode='T{$prodCode}' // without prefix
)
AND AllowSalesOrder = ''";
Wild-card by single character
OR prodCode LIKE '{$prodCode}_' // single character wild-card
It seems that you may have products with the same number but not the same suffix? like 8512n and 8512h ?
You could use LIKE '%$code%'
$sql = "SELECT * FROM isproducts WHERE prodCode LIKE '%".$prodCode."%' AND AllowSalesOrder = ''";
and I think its the more secure way in your case, so that all products containing the number will appear, regardless of suffix or prefix.
The above may return more than one product, so the user still has to choose which one it is he is actually looking for.
You can use % wild card for this kind of problem.
check this out....
http://www.w3schools.com/SQL/sql_wildcards.asp
it might be useful... :-)
In some of my code I use the following strategy:
# psuedo-code ... NOT intended for real use:
SELECT COUNT(*) FROM someTable WHERE someColumn = "{XXX}"
# If that returns exactly one than use the corresponding query
SELECT COUNT(*) FROM someTable WHERE someColumn LIKE "{XXX}"
# If that returns exactly one then use it
SELECT COUNT(*) FROM someTable WHERE someColumn LIKE "{XXX}%"
# If that returns exactly one then use it
SELECT COUNT(*) FROM someTable WHERE someColumn LIKE "%{XXX}%"
# If that returns exactly one then use it
... (where {XXX} is the placeholder for the user supplied search term).
The idea here is that I first try a precise match, then I try it under the assumption that the term already may contain SQL wildcards, then I try suffixing the % wild card and finally I try wrapping it with % wild cards.
At any point if I've found an unambiguous match then I use it. If I find more than one match at any point (not shown in the psuedo-code here) then I might throw an exception or I might return them or a subset of them based on the specifics of what I'm doing.
(In reality I'm using the parameter interpolation features of Python or Perl or sanitizing my inputs to allow wild cards while preventing SQL injections; so the code doesn't look like what I'm showing here. This is just to convey the general idea).
My goal is to allow my scripts to be called with the minimum unambiguous arguments supplied which sounds roughly similar to what your clients are requesting here.
From a usability perspective most users will get the first characters of any input right. So exact match following by suffixed wildcard match is most likely to succeed most of the time. In my case my users are likely to be familiar with SQL wildcards and may prefer to use them to construct their own unambiguous match; and logically that attempt has to be inserted before I start suffixing or wrapping it with my own wildcards.
This is why I use this specific sequence of matching attempts.

Categories