Why Laravel5.2 eloquent query builder showing different results in windows and linux? - laravel

I have a field named 'status' in my table and the field type is 'tinyint', When I am trying to perform the where condition using eloquent query builder, it gives me different results in linux and windows.
Table structure:
and my code is,
Os::all()->where('status', 1);
It is working fine on windows machines and I am getting the results, but in Linux it returns empty collection.
then I tried
Os::all()->where('status', '1');
Its working fine on Linux, but not in windows :(
When I set the 'strict' flag to 'false'
Os::all()->where('status', 1, false);
it working fine on both platforms, Why is it so ?

I think that problem is in 'status' variable type.
First of all, I want to be sure that you know that your condition where('status', 1) is not on eloquent query builder but on Collection.
Os::all() query returns Collection of elements and method where('status', 1) is working on this Collection (set of database data). Where condition on collection != where condition in query builder. If you want to make where on query builder please do it in this way:
Os::where('status', 1)->get();
I guess that problem is that in Windows 'status' variable is casting to integer but in Linux is casting to string. This is due to slightly diffrent mysql configuration. Now take a look at Collection where method and everything will be clear:
public function where($key, $value, $strict = true)
{
return $this->filter(function ($item) use ($key, $value, $strict) {
return $strict ? data_get($item, $key) === $value
: data_get($item, $key) == $value;
});
}
When $strict = true method dont look at variable types.
How fix it?
You have probably few ways:
Unify mysql configuration - it could be hard.
Try query builder where: Os::where('status', 1)->get() - maybe in this way type casting will not be problem
Write attribute getter to change status attribute type in each time it is requested:
public function getStatusAttribute($value)
{
return (int)$value;
} You need choose to cast to (string) or (int). Keep in mind that status with changed type will not be visible in Collection but
only in single model obiect. After adding this getter you will not
need to add 'false' attribute to Collection where.

Related

Accessing relation after query not returning correct values

I have a kind of stupid problem (in my opinion since I am sure this is on my end and I simply overlook sth).
I query a model with a relation to it. When I simply return the whole result everything works fine.
$myModel = SampleModel::where('id', '>=', 0)->with('relationToAnother')->get();
returning this works as expected.
However, when I try to access the relation while looping over it after I got it back like
foreach($myModel as $m) {
echo $m->relationToAnother;
}
I simply get back the value of my original modal that is responsible for the connection (e.g. the column from SampleModel and not the relationship).
I am missing sth here and I dont know what.
This is what I am doing after I got $myModel back.
$returnValue = array();
foreach ($myModel as $v) {
$returnValue[] = $v->relationToAnother->subProperty;
}
}
which throws the error Trying to get property 'subProperty' of non-object. Which is just logic because $v->relationToAnother is still just the value of a column from my model without the relation and not the relation itself.
I didnt include the checks if that property is not null on purpose because that isnt the issue here.
I think this will work
$myModel = SampleModel::with('relationToAnother')->where('id', '>=', 0)->get();

Laravel Scout/Meilisearch - filter by a non-searchable column

I want to make it so that I can filter results based on a column that isn't searchable with Meilisearch and Laravel scout.
So imagine a "Comment" table, with the following searchable columns:
public function toSearchableArray() {
$array = Arr::only(
$this->toArray(),
['id','title', 'link', 'raw_text', 'subchan', 'nsfw']
);
return $array;
}
But only get results past a certain date:
Comment::search($query, ['filters' => 'created_at > 795484800'])
To do this, I need to add created_at scout's toSearchableArray. The problem with this is that when a user searches, results from created_at will also be queried.
If I understand you correctly you want to be able to filter based on the created_at column, but it shouldn't be searchable, ie entering "795" as a query shouldn't return all results where "795" is part of the timestamp?
I don't think Scout will allow you to achieve this in a simple way at the moment but it should still be possible.
Step 1 is to add the created_at column to the toSearchableArray() method. This will ensure the data is indexed by Meili.
Step 2 is to alter the configuration of the index where your model is searchable in order to exclude created_at from the list of searchable attributes. This is psuedo code and undocumented but it should look something like this:
$dummy = new Comment();
// Should resolve to an engine: https://github.com/laravel/scout/blob/f8aa3c3182fe97f56a6436fd0b28fcacfcbabc11/src/Searchable.php#L279
$engine = $dummy->searchableUsing();
// Should resolve to MeiliSearch\Endpoints\Indexes via a magic method that resolves the underlying Meili driver:
// https://github.com/laravel/scout/blob/33bbff0e3bfb1abd0ea34236c331fc17cdeac0bc/src/Engines/MeiliSearchEngine.php#L298
// ->
// https://github.com/meilisearch/meilisearch-php/blob/f25ee49b658f407af3d3f1f9a402997e7974b6bb/src/Delegates/HandlesIndex.php#L23
$index = $engine->index($dummy->searchableAs());
// https://github.com/meilisearch/meilisearch-php/blob/f25ee49b658f407af3d3f1f9a402997e7974b6bb/src/Endpoints/Delegates/HandlesSettings.php#L55
$index->updateSearchableAttributes(
['id','title', 'link', 'raw_text', 'subchan', 'nsfw']
);
Once created_at is indexed but not searchable you want to filter on the value. Meili has operators for numeric values.
Step 3 is to do a custom search using Scout:
Comment::search($query, function (Indexes $meilisearch, $query, $options) {
$options['filters'] = 'created_at>795484800';
return $meilisearch->search($query, $options);
});
Again, this is pseudo code – I haven't tested any part of it. I would really appreciate if Scout would implement support for customizing the index' settings on creation or exposing a method for updating the settings, allowing you to add driver specific settings in your configuration file for example.
i spent numerous hours debugging and getting the filter for dates to work.
this wont work as the where clause only accepts two arguments
Comment::search($query)->where('created_at', '>', 795484800)->get();
this also wont work because the arguments passed are not part of the two options that they have supported in the scout library
Comment::search($query, function (Indexes $meilisearch, $query, $options) {
$options['filters'] = 'created_at>795484800';
return $meilisearch->search($query, $options);
});
my solution for everyone out there trying to get this to work is to use the following:
$results = Event::search(
query: $request->get('query'),
callback: function (Indexes $meilisearch, $query, array $options) use ($request, $from, $to) {
$options['filter'] = "from <= 1667692800";
// dd($options);
return $meilisearch->rawSearch(
$query,
$options,
);
},
)->paginate();
hope this helps anyone else having issues as this wasted my morning searching for solutions until i decided to dig into the code in the library.
I solved my problem by using filterable attributes of Meilisearch. But it needs to be configured before running the search. I used php artisan tinker to solve this as follows, you might want to write an artisan command to do so.
$client = new MeiliSearch\Client('https://url_to_meilisearch_instance:7700');
$client->index('comments_index')->updateFilterableAttributes(['created_at']); // Replace your index_name
And that's about it. If you have a rather large dataset, you might want to run the following command to check the status:
$client->index('comments_index')->stats();
If the response contains isIndexing => false you're good to go. Now you may run the filter as usual,
Comment::search($query)->where('created_at', '>', 795484800)->get();

How to know what columns are presents in a Eloquent query before to execute it in Laravel 5.5?

Im using Laravel 5.5 and I have and QueryBuilder object (from the "Illuminate/Database/Eloquent/Builder" class).
I want to set an orderBy sentence into my query, but only if this field is present and exists in the QueryBuilder object (as column in the select section sentence).
For example, there is an User model, with the following fields ['id', 'firtsname', 'lastname', 'username','description'].
This is my object:
Use App\User;
$query = User::query();
if ($request->input('sort') != null) {
$model_query->orderBy($request->input('sort'), 'ASC');
}
$users = $query->get();
When I execute it, works fine (if I send you consistent data, of course). But if I set a column what does not exists, it sends and exception. So, the question is, how I can get the columns to retrieve from my $query object? To validate it, and if it's presents, execute the ordening code.
To answer your question, you can get the presence status of a column using Schema::hasColumn()
if (Schema::hasColumn('users', $request->sort)) {
//
}
GOING FURTHER
Now this doesn't seem very efficient, and maybe potentially leak data. Better validating your sort input and accept only proper column names:
$request->validate(['sort' => 'in:column1,column2']);

How to make conditions in a laravel query, based on returned value of a helper function

I'm using Laravel 5.7. I have a 'Match' model whose first participant name is returned from a helper function getFirstParticipant(Match $match).
I am trying to get certain matches from DB whose first participants are e.g. 'John'. Is it possible that I use Eloquent query functions to do so for example something like this?
Match::where('firstParticipant', 'John')
or any other solutions?
I am copying my helper function below if it can help to declare the problem:
function getFirstParticipant(Match $match)
{
$structure_id = $match->structure_id;
$seed = $match->matchResult->first_seed;
$placement = Placement::where('structure_id', $structure_id)->where('seed', $seed)->first();
return !empty($placement->player_id) ? $placement->player->username : $placement->team->name;
}
Just sub a variable into the second parameter.
$first = getFirstParticipant(Match $match);
Match::where('firstParticipant', $first->first_name)->get();
Obviously I'm assuming some variable names here such as first_name. But you should be able to follow that. Just use your helper to return a model value and insert that returned value as the second parameter of the where argument.
No, there is no way. Database design issues aside, the only way I can see this working is by using collection methods.
Match::get()->filter(function ($match) {
return $match->firstParticipant == 'John'; // Or is it getFirstParticipant($match) ?
})->all();
# Laravel 6.x introduces LazyCollections
Match::cursor()->filter(function ($match) {
return $match->firstParticipant == 'John'; // Or is it getFirstParticipant($match) ?
})->all();

Does "where" works with mutattors in eloquent?

I have a mutattor in an eloquent model that generates a "status" atribute.
public function getStatusAttribute(){
if(){
return "enabled";
}
else
{
return "disabled";
}
}
Can I use?
$query = Anuncio::query();
$query->where('status', "enabled" );
return $query->get();
I seems that I cannot. I getting "status" column not defined. How can I get around this problem?
No doesn't works it works on model level
you can use after query from database in collection result
No, when you are doing a query you are asking the database, therefor there needs to be a column status.
There is a way, retrieve x elements from the database and use the Laravel Collection method where(). This is not optimal if you have many elements in teh database, it will retrieve all of them.
Anuncio::all()->where('status', 'enabled')->all();

Resources