Use identifier in PHP and how does it affect the logic - php

Use identifier is used when we want to send some outer environment variables inside closures so I have a scenario.
$invoiceList = UserInvoice::where('user_id', $this->user->id)
->where(function ($query) use ($filters) {
$query->where('invoice_number', 'LIKE', "%{$filters['invoiceNumber']}%")
->when($filters['type'], function ($query) use ($filters) {
$query->whereIn('invoice_type', $filters['type']);
});
});
In this Laravel eloquent query I have checked for filter['type'] when it is there then try to filter my invoice_type from filter[type]
But I am now confused here that what if I pass filters inside functions parameters like this
method 1
->when($filters['type'], function ($query,$filters) use () {
$query->whereIn('invoice_type', $filters['type']);
});
Or
method 2
->when($filters['type'], function () use ($query,$filters) {
$query->whereIn('invoice_type', $filters['type']);
});
What impact would it be if I opt these methods
I have also tried both and in method 1 it throws me an error that filters['type'] not available
and when I tried method 2 it works fine so please can any one explain how does it works I mean not theoretically but in practical language what basically happening there.
Also where it is defined that a closure function will accept how many numbers of arguments

The very last thought in your question is actually the key to your misunderstanding:
where it is defined that a closure function will accept how many numbers of arguments
The answer is wherever the function is eventually executed.
In this case, you are passing the function to the when method; that method can decide when to execute it, and what parameters to pass when it does. We can look at the source code directly and see what happens:
public function when($value, callable $callback = null, callable $default = null)
{
// some extra logic skipped as not relevant right now
if ($value) {
return $callback($this, $value) ?? $this;
} elseif ($default) {
return $default($this, $value) ?? $this;
}
return $this;
}
So, if $value is "truthy", $callback will be executed with exactly two parameters: the current object ($this) and the exact value provided ($value). In this case, that will be the query you're building, and the value of $filters['type'].
Think of it like handing over an envelope: you don't have any control of what the when method puts into the envelope, you just get to look at it once it's there.

Related

Method Illuminate\Support\Collection::find does not exist

Edit function:
public function editCheck($id, LanguagesRequest $request)
{
try{
$language = language::select()->find($id);
$language::update($request->except('_token'));
return redirect()->route('admin.languages')->with(['sucess' => 'edit done by sucsses']);
} catch(Exception $ex) {
return redirect()->route('admin.addlanguages');
}
}
and model or select function
public function scopeselect()
{
return DB::table('languages')->select('id', 'name', 'abbr', 'direction', 'locale', 'active')->get();
}
This code is very inefficient, you're selecting every record in the table, then filtering it to find your ID. This will be slow, and is entirely unnecessary. Neither are you using any of the Laravel features specifically designed to make this kind of thing easy.
Assuming you have a model named Language, if you use route model binding, thing are much simpler:
Make sure your route uses the word language as the placeholder, eg maybe your route for this method looks like:
Route::post('/languages/check/{language}', 'LanguagesController#editCheck');
Type hint the language as a parameter in the method:
public function editCheck(Language $language, LanguagesRequest $request) {
Done - $language is now the single model you were afer, you can use it without any selecting, filtering, finding - Laravel has done it all for you.
public function editCheck(Language $language, LanguagesRequest $request) {
// $language is now your model, ready to work with
$language::update($request->except('_token'));
// ... etc
If you can't use route model binding, or don't want to, you can still make this much simpler and more efficient. Again assuming you have a Language model:
public function editCheck($id, LanguagesRequest $request) {
$language = Language::find($id);
$language::update($request->except('_token'));
// ... etc
Delete the scopeselect() method, you should never be selecting every record in your table. Additionally the word select is surely a reserved word, trying to use a function named that is bound to cause problems.
scopeselect() is returning a Collection, which you're then trying to filter with ->find() which is a method on QueryBuilders.
You can instead filter with ->filter() or ->first() as suggested in this answer
$language = language::select()->first(function($item) use ($id) {
return $item->id == $id;
});
That being said, you should really find a different way to do all of this entirely. You should be using $id with Eloquent to get the object you're after in the first instance.

PHP Closures - Getting class name of closure scope origin

Case
I am playing around on a laravel project to see if i can use closures for my implementation of a sorting interface, and i noticed that when i dd() my closure, it also shows the class in which the closure was created as a property.
Minimised Code
// in my Order model class, i have a function that will return a closure
public static function defaultSortFunction(){
$sortColumn = property_exists(self::class,'defaultSortingColumn') ? self::$defaultSortingColumn : 'created_at';
return function($p,$n)use($sortColumn){
return $p->$sortColumn <=> $n->$sortColumn;
};
}
// in one of my controller I use for testing, I added these 2 methods for testing
public function index(){
$sortFunction = Order::defaultSortFunction();
$this->someOtherFunction($sortFunction);
return 'done';
}
private function someOtherFunction($fn){
dd($fn);
// $scopeModel = get_class($fn); => Closure
// example of how I can use this value later
// $scopeModel::take(10)->get()->sort($fn);
}
The result of the dd() inside someOtherFunction():
^ Closure($p, $n) {#1308 ▼
class: "App\Order"
use: {▼
$sortColumn: "created_at"
}
}
Question
From the result of the dd() it shows that the closure has a property that shows that it was defined in the class App\Order. Is there any way to access this value?
I have tried get_class($fn) but as expected it gives "Closure", and if i did $fn->class it gives an error saying Closure object cannot have properties.
You may use Reflection API on your closure which is a much cleaner way than debug_backtrace
// in one of my controller I use for testing, I added these 2 methods for testing
public function index(){
$sortFunction = Order::defaultSortFunction();
$this->someOtherFunction($sortFunction);
return 'done';
}
private function someOtherFunction($fn){
$reflectionClosure = new \ReflectionFunction($fn);
dd($reflectionClosure->getClosureScopeClass()->getName());
}
getClosureScopeClass returns a ReflectionClass instance based on the class you need to find and getName finishes the job.
You can of course inject the class name in to the closure via a parameter in your defaultSortFunction, but that's obviously not so nice.
You should be able extract the calling class yourself from the call stack using:
https://www.php.net/manual/en/function.debug-backtrace.php
If you use the limit parameter you should be able to restrict it to only returning the calling class and no further back.
I don't know for sure, but I suspect it isn't particularly performant.

Dynamic model filter in Laravel's Eloquent

I'm looking for a way to make a dynamic & global model filter in Laravel.
I'm imagining a function like the following in my User.php model:
public function filter() {
return ($someVariable === true);
}
Whenever I do a query using Eloquent's query builder, I only want users to show up in the collection when the filter above returns true. I would have thought a feature like that existed, but a quick look at the documentation suggests otherwise. Or did I miss it?
I believe what you're looking for is Query Scopes.
They are methods that may be defined in a global or local context, that mutate the current query for a given model.
https://laravel.com/docs/5.5/eloquent#query-scopes
For example:
Lets say I have a database table called "Teams" and it has a column on it called "Wins." If I wanted to retrieve all Teams that had a number of Wins between Aand B I could write the following Local scope method on the teams model:
public function scopeWinsBetween($query, int $min, int $max)
{
return $query->whereBetween('wins', $min, $max);
}
And it could be invoked as such:
$teams = Teams::winsBetween(50, 100)->get();
I think you could use Collection macro but you will need to suffix all your eloquent get(); to get()->userDynamicFilter();
Collection::macro('userDynamicFilter', function () {
//$expected = ...
return $this->filter(function ($value) use($expected) {
return $value == $expected;
});
});
Thanks. For now I've simply added a post filter option to the models using the following code:
// Apply a post filter on the model collection
$data = $data->filter(function($modelObject) {
return (method_exists($modelObject, 'postFilter')) ? $modelObject->postFilter($modelObject) : true;
});
in Illuminate/Database/Eloquent/Builder.php's get() function, after creating the collection. This allows me to add a function postFilter($model) into my model which returns either true or false.
Probably not the cleanest solution but a working one for now.

Passing function as an argument of another function in PHP

I want to pass function (not the result) as an argument of another function. Let's say I have
function double_fn($fn)
and i want to run given function ($fn) two times. Is there such mechanism in PHP? I know you in python function is a kind of variable but is PHP similar?
#edit
is there similar way to use methods?
function double_fn(parrot::sing())
Since 5.3 (note) you do this with closures quite naturally:
function double_fn(callable $fn)
{
$fn();
$fn();
}
double_fn(function() {
echo "hi";
});
(note) type hint only since 5.4
The callable type can be a few things:
a string comprising the function name to call,
a closure (as above)
an array comprising class (or instance) and method to call
Examples of the above is explained in the manual.
Update
edit is there similar way to use methods?
function double_fn(parrot::sing())
Doing that will pass the result of parrot::sing() into the function; to pass a "reference" to the static method you would need to use either:
double_fn('parrot::sing');
double_fn(['parrot', 'sing']);
It's... a bit different. Pass the function name as a string.
function foo()
{
echo "foobar\n";
}
function double_fn($fn)
{
$fn();
$fn();
}
double_fn('foo');
You can use anonymous functions introduced in PHP 5.3.0:
function double_fn($fn) {
$fn();
}
double_fn(function(){
// fonction body
});
or:
$myFn = function(){
// fonction body
};
double_fn($myFn);

$provider = function() vs function provider()

I've seen in various coding examples different coding style when creating functions.
What is the difference between creating a function using
$provider = function() { code here }
vs
function provider(){ code here }
Is the first example simply a short version of: $data = provider(); ?
When do we use the first example?
No, it isn't. First code is declaration of closure, i.e. anonymous function. It has no name and can be called with identifier that holds it. Second sample is normal function (user-defined function, to be more specific) and, thus, it will be accessible within all scopes via it's name - not like closure, which will be available for calling only within scope, where it was defined.
You can have as many closures as you wish - they are just callable entities, for example this is valid:
$provider = function() { Code here }
$another = function() { Code here } //same code
-and calling $provider (for example, with call_user_func()) will have nothing to do with $another
Another significant difference is that closure can accept context parameters like:
$provider = function() use ($param1, $param2, ...) { Code here }
-so inside it's body context parameters will be available. Context parameters are not like usual arguments - since context parameters defined and exist independent from closure while arguments are evaluated at the moment, when call happened.
First declaration is anonymous function.And after assignment,we have variable with name $provider and can call $provider() .Second declaration its just normally function.
Anonymous function can be user for example in array_map,array_filter.For example
$a = array(1, 2, 3, 4, 5);
$res = array_filter(
$a, function ($elem) {
return $elem > 3;
}
);
print_r($res);
output element who larger 3

Categories