Laravel Eloquent - text field with leading number getting interpreted as integer - laravel

I have the following code:
$event = Event::where('slug', '=', $param)
->orWhere('eventID', '=', $param)
->firstOrFail();
The purpose is to enable $event to populate either with its id or a short text string in the slug field.
I'm experiencing an issue where text beginning with a number is causes the $event to get the value where eventID = the numeric part of the text.
This is not the desired behavior.
Ideas? Thanks!

You need check $param for integer value and depends on that make a query. It easy to do with when method for laravel versions > 5.3. For older version logic will be the same but don't use when method because it has not third parameter.
$event = Event::when(filter_var($param, FILTER_VALIDATE_INT) !== false,
function($query) use ($param){
return $query->where('eventID', $param);
},
function($query) use ($param){
return $query->where('slug', $param);
}
)->firstOrFail();
Other way:
$event = Event::query();
$event = filter_var($param, FILTER_VALIDATE_INT) !== false ? $event->where('eventID', $param) : $event->where('slug', $param);
$event = $event->firstOrFail();

Related

Orwhere has method does not allow null

enter image description hereI am trying to implement a many to many relationship search with 2 models.
i get input from multiple checkbox values and want to search for items that match A or B when there is an input of data.
I read this url and wrote the same logic.
https://laracasts.com/discuss/channels/laravel/many-to-many-relationship-with-2-pivot-table-data-search
public function search(Request $request)
{
$languages = $request->lang;
$fields = $request->field;
$agencies = Agency::with('languages')->with('specialized_fields')
->orWhereHas('languages', function($query) use ($languages) {
$query->whereIn('language_id', $languages);
})
->orWhereHas('specialized_fields', function($query) use ($fields) {
$query->whereIn('specialized_field_id', $fields);
})
->get();
dd($agencies);
}
i expected to achieve A or B search but instead I got this error.
Argument 1 passed to Illuminate\Database\Query\Builder::cleanBindings() must be of the type array, null given, called in /var/www/jtf/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php on line 907
it seems that it causes this error if either A or B is null, but why??? Does the OrWhereHas method work only when theres an input??
/added info/
my error message
my agency model
class Agency extends Model {
protected $guarded = [
'id'
];
public function languages(){
return $this->belongsToMany('App\Language');
}
public function specialized_fields(){
return $this->belongsToMany('App\SpecializedField');
}
public function region(){
return $this->hasOne('App\Region');
} }
I believe it's because either $languages or $fields is null.
Since ->whereIn() is expecting an array, but you're passing null.
You just need to make sure you're passing an array.
$languages = array_filter((array) $request->lang); // cast to array & remove null value
$fields = array_filter((array) $request->field);
$agencies = Agency::with('languages', 'specialized_fields')
->orWhereHas('languages', function($query) use ($languages) {
$query->whereIn('language_id', $languages);
})
->orWhereHas('specialized_fields', function($query) use ($fields) {
$query->whereIn('specialized_field_id', $fields);
})
->get();
I'm speculating that you started your where query chain with an orWhereHas() which may have caused the problem, try starting with whereHas() instead.
public function search(Request $request){
$languages = $request->lang;
$fields = $request->field;
$agencies = Agency::with('languages', 'specialized_fields') // you can get away by just using one with(), not needed but its cleaner this way
->whereHas('languages', function($query) use ($languages) { // previously orwherehas
$query->whereIn('language_id', $languages);
}) ->orWhereHas('specialized_fields', function($query) use ($fields) {
$query->whereIn('specialized_field_id', $fields);
})
->get();
dd($agencies);
}

Use more complex where clauses in Scout

I am a happy user of Laravel Scout.
Now I'd like to extend my search:
$data
= new UserOverviewResourceCollection(User::search($searchphrase)
->currentStatus('active')->orderBy('lastname', 'asc')
->orderBy('firstname', 'asc')
->paginate(config('pagination.length')));
currentStatus comes from https://github.com/spatie/laravel-model-status .
Now I am getting a response, that currentStatus is not supported. I thought I could be a good idea to filter the result of User::search after it has been returned from scout?
Another idea: I'd like to add more complex where clauses:
->where([
[
'starts_on',
'<',
Carbon::now()->toDateTimeString(),
],
[
'ends_on',
'>',
Carbon::now()->toDateTimeString(),
],
])
Maybe you got a better idea?
I really need similar functionality as well but I'm afraid it's not possible at the moment (Laravel 8)
According to the docs; Laravel\Scout\Builder (which is what search() returns) only has a really basic implementation of the where method an can only handle exactly 2 arguments.
Here is the code from the scout package:
/**
* Add a constraint to the search query.
*
* #param string $field
* #param mixed $value
* #return $this
*/
public function where($field, $value)
{
$this->wheres[$field] = $value;
return $this;
}
See https://laravel.com/docs/8.x/scout#where-clauses
Putting the search() behind all regular eloquent builder methods won't work either since search will only be a static function of the model.
Hopefully this will be improved in the future.
You can extend Builder and add currentStatus method to store required status in builder. See example
https://github.com/matchish/laravel-scout-elasticsearch/issues/47#issuecomment-526287359
Then you need implement your own engine and build query using builder there.
Here is example for ElasticSearch engine
https://github.com/matchish/laravel-scout-elasticsearch/blob/06c90914668942b23ffaff7d58af5b9db1901fb1/src/Engines/ElasticSearchEngine.php#L135
Well rewriting it as this should work.
$data
= new UserOverviewResourceCollection(User::search($searchphrase)
->where('starts_on','<',now())
->orderBy('lastname', 'asc')
->orderBy('firstname', 'asc')
->paginate(config('pagination.length')));
Notice that now() is just a global helper function returning current moment Carbon instance. It's just shorter to write, no other reason for using it.
And if you want grouped where queries you do it like this:
$data
= new UserOverviewResourceCollection(User::search($searchphrase)
->where(function($q) {
$q->where('starts_on','<',now())->where('ends_on','>',now())
})
->orderBy('lastname', 'asc')
->orderBy('firstname', 'asc')
->paginate(config('pagination.length')));
And then you should be able to export this to the UserOverviewResourceCollection as a local scope, for example:
public function scopeActive($q) {
return $q->where('starts_on','<',now())->where('ends_on','>',now())
}
and then you can use something like this:
$data
= new UserOverviewResourceCollection(User::search($searchphrase)
->active()
->orderBy('lastname', 'asc')
->orderBy('firstname', 'asc')
I've written this from my head so there might be typos.
$data
= new UserOverviewResourceCollection(User::search($searchphrase)
->currentStatus('active')->orderBy('lastname', 'asc')
->where(function($q){
$query->where('starts_on', '<',Carbon::now()->toDateTimeString());
$query->where('ends_on', '>',Carbon::now()->toDateTimeString());
})
->orderBy('firstname', 'asc')
->paginate(config('pagination.length')));
Try this code. This will satisfy your complex where condition

Laravel - Filter out 'null' values from request

I am attempting to filter my $request to not include any input data where the qty field is null (qty sits on a pivot table)
I have already attempted:
$qty = $request->input('qty'); //define qty
$filtered = $request->filter(function ($qty, $key) {
return $qty != null;
});
$filtered->all();
but I receive the error Method Illuminate\Http\Request::filter does not exist, which is strange because I have use Illuminate\Http\Request; in the Controller.
Although I have never filtered data before so I don't know if this is even the correct approach.
I'm pretty positive the $request instance doesn't have a filter method. I think you can achieve what you want using the collect helper:
$input = collect(request()->all())->filter(function($value) {
return null !== $value;
})->toArray();
Update: As of Laravel 8.62.0 the collect method exists on the request:
$input = request()->collect()->filter(function($value) {
return null !== $value;
})->toArray();

Property [***] does not exist on this collection instance Laravel eloquent relationship

In my Post Model
public function user()
{
return $this->belongsTo('App\User');
}
And in the User Model
public function posts()
{
return $this->hasMany('App\Post');
}
Now I am trying to get the comments of a specific user
$user= User::where('name', 'like', '%Mat%')->first();
return $user->posts->comment;
But it shows
Property [comment] does not exist on this collection instance.
The user has many posts which therefore returns a collection, you will need to loop over this to get your comments out. I.e.
$user = User::where('name', 'like', '%Mat%')->first();
$user->posts->each(function($post) {
echo $post->comment;
});
See the documentation on Laravel Collections
I think you can try this :
$user= User::with('post')->where('name', 'like', '%Mat%')->get();
$postComment = array();
foreach($user->post as $post){
$postComment = $post->comment;
}
return $postComment;
Hope this help for you !!!
If you want to have all comments you can use the following code:
$comments = [];
$user = User::where('name', 'like', '%Mat%')->with(['post.comment' => function($query) use (&$comments) {
$comments = $query->get();
}])->first();
return $comments;
Property [comment] does not exist on this collection instance.
The above error occurs because the Posts function returns a collection. Now you will have to traverse through each element of the collection.
Since, you are returning $user->posts()->comment, I am assuming you need it in the form of an array and don't have to simply echo them out, one by one. So you can store them all in an array & then process it whatever whay you like.
$comments = array();
$user->posts()->each(function $post){
$comments = $post->comment;
}
return $comments;
For greater insight, into this collection function read:
https://laravel.com/docs/5.4/collections#method-each

Eloquent - where not equal to

I'm currently using the latest Laravel version.
I've tried the following queries:
Code::where('to_be_used_by_user_id', '<>' , 2)->get()
Code::whereNotIn('to_be_used_by_user_id', [2])->get()
Code::where('to_be_used_by_user_id', 'NOT IN', 2)->get()
Ideally, it should return all records except user_id = 2, but it returns blank array. How do I tackle this?
Code::all()
This returns all 4 records.
Code model:
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class Code extends Model
{
protected $fillable = ['value', 'registration_id', 'generated_for_user_id', 'to_be_used_by_user_id', 'code_type_id', 'is_used'];
public function code_type()
{
return $this->belongsTo('App\CodeType');
}
}
Use where with a != operator in combination with whereNull
Code::where('to_be_used_by_user_id', '!=' , 2)->orWhereNull('to_be_used_by_user_id')->get()
For where field not empty this worked for me:
->where('table_name.field_name', '<>', '')
While this seems to work
Code::query()
->where('to_be_used_by_user_id', '!=' , 2)
->orWhereNull('to_be_used_by_user_id')
->get();
you should not use it for big tables, because as a general rule "or" in your where clause is stopping query to use index. You are going from "Key lookup" to "full table scan"
Instead, try Union
$first = Code::whereNull('to_be_used_by_user_id');
$code = Code::where('to_be_used_by_user_id', '!=' , 2)
->union($first)
->get();
Or like this:
Code::whereNotIn('to_be_used_by_user_id', [2])->get();
Fetching data with either null and value on where conditions are very tricky. Even if you are using straight Where and OrWhereNotNull condition then for every rows you will fetch both items ignoring other where conditions if applied. For example if you have more where conditions it will mask out those and still return with either null or value items because you used orWhere condition
The best way so far I found is as follows. This works as where (whereIn Or WhereNotNull)
Code::where(function ($query) {
$query->where('to_be_used_by_user_id', '!=' , 2)->orWhereNull('to_be_used_by_user_id');
})->get();
Here's a useful scope method that you can add to your model and use it for different columns.
/**
* #param Builder $query
* #param string $field
* #param $value
* #return Builder
*/
public function scopeWhereNot(Builder $query, string $field, $value): Builder
{
return $value === null
? $query->whereNotNull($field)
: $query->where(function ($q) use ($field, $value) {
return $q->where($field, '<>', $value)
->orWhereNull($field);
});
}
and use it as follows
Code::whereNot('to_be_used_by_user_id', 2)->get()

Resources