Laravel eloquent: How does relation inherit functions from query builder? - laravel

how do they achieve, that Illuminate\Database\Eloquent\Relations\Relation has access to all query builder functions?
I see they have a $query property, but does not explain how all its methods are available inside relation

If you are aware of php magic methods then you will know __call method
this method will be called when you initialize a php object and you try to call a method which is not available in the class. By using __call method from the Illuminate\Database\Eloquent\Relations\Relation class they are forwarding the call to Illuminate\Database\Eloquent\Builder. I will explain it very clearly by pointing out the code.
Inside the laravel framework there is a trait named as ForwardsCalls . This trait is used in many classes to handle the call forwarding to another class.
So here is how the call from the Relation class is forwarded to Builder class. While initilting the new Relation class Builder class will be initialized. So when you try to call a method from reltion class which is not available it will call __call method. After that it will look for a available macros . So when a macros method is not found. Then it will use forwardDecoratedCallTo from ForwardsCalls Trait.
So forwardDecoratedCallTo will accept 3 arguments namely $object, $method and $parameters. Whereas
$object will be $this->query which has a Illuminate\Database\Eloquent\Builder instance.
$method will be the method the you try to access from Builder Method.
$parameters will be the all the parameters that is be passed to the method.
I will try to Demonstrate will the example without the traits and helpers from laravel
class ClassTwo {
public function classTwoMethodOne()
{
dd(__FUNCTION__.' has been called');
}
public function classTwoMethodTwo()
{
dd(__FUNCTION__.' has been called');
}
public function classTwoMethodThree()
{
//you cannot call this method dynamically
dd(__FUNCTION__.' has been called');
}
}
class ClassOne {
public function classOneMethodOne()
{
dd(__FUNCTION__.' has been called');
}
public function classOneMethodTwo()
{
dd(__FUNCTION__.' has been called');
}
public function __call($methodName, $arguments)
{
$methodsTobeForwarededtoClassTwo = [
'classTwoMethodOne',
'classTwoMethodTwo',
// 'classTwoMethodThree'
//i have commented this method so you cannot access it
//from dynamic calls
];
if(in_array($methodName,$methodsTobeForwarededtoClassTwo))
{
return (new ClassTwo)->{$methodName}($arguments);
}
dd(sprintf(
'Call to undefined method ClassTwo::%s()', $methodName
));
}
}
So here comes the testing part.
$classOneobj = new ClassOne;
Basic Test
dump($classOneobj->classOneMethodOne()); will output as classOneMethodOne has been called
dump($classOneobj->classOneMethodTwo()); will output as classOneMethodTwo has been called
Dynamic Call Test
dump($classOneobj->classTwoMethodOne()); will output as classTwoMethodOne has been called
dump($classOneobj->classTwoMethodTwo()); will output as classOneMethodTwo has been called
dump($classOneobj->classTwoMethodThree()); will output as Call to undefined method ClassTwo::classTwoMethodThree() Because i have commented that method in __call function in ClassOne.
If you still need clarity please post a comment

Related

Accessing Property of a method from another method inside a class in Laravel

I want to Access the property "na" of "ha" public method from "test" method in same class. But I am getting error "Trying to get property 'ni' of non-object"
public function ha(){
$ni = 'fs';
$nin = 'adfsfsfs';
}
public function test()
{
echo $this->ha()->ni;
}
Variables inside a method are only available inside that method. You should either define on your class or return the values from the method and call the method to get the values. Also please note that this is not about laravel, but it's about php. For more information about variable scope: check this

How to call block function in other custom module's model file?

I am working on Magento 2. I want to use other module's block function to get some data. How can I call that block function in my module's model file ?
The two ways to call you a block class method in custom module class
1) using constructor dependency
like that
public function __construct(Namespace\ModuleName\Helper\Data $helper)
{
$this->helper = $helper;
}
public function MyFunction()
{ $this->helper->HelperDemo();
}
2) using method dependency
public function execute(Namespace\ModuleName\Helper\Data $helper)
{
$helper->HelperDemo();
}
for more detail visit the below link
https://devdocs.magento.com/guides/v2.3/extension-dev-guide/depend-inj.html

Access class from helper in controller Laravel

I created an helper and I try to use it in one of my controllers,but I got an error, and I am not sure why.
//StringHelper.php
namespace App\Helpers;
class StringHelper
{
public function example($str1){
//CODE
}
}
//config/app.php
'aliases' => [
'StringHelper' => App\Helpers\StringHelper::class,
]
//In controller
use StringHelper;
$percentage = StringHelper::example($title);
Non-static method App\Helpers\StringHelper::example() should not be
called statically
Because the method example($str1) is not static, you need to call it by instance.
I think you are calling other instance's methods in example, so the simple way is call the method by instance.
$helper = new StringHelper();
$percentage = $helper->example($title);
Or you need to defined all those methods to static.

Pass object in all methods of all controllers in Laravel

I have this code in the HomeController#index:
$towns = Town::all();
return Redirect::to('home')
->with('towns', $towns);
Is there any way I can tell Laravel to execute that lines of code before the end of methods and controllers I define without me copying and pasting those lines of code in every method?
You don't need to do that, you can just share this data with all views by using the view()->share() method in a service provider:
view()->share('towns', Town::all());
You can also use a view composer for that:
public function compose(View $view)
{
$view->with('towns', Town::all());
}
You can extend all controllers from your basic controller. Use Controller.php on app/Http/Controllers/controller.php or create new one.
Add myThreeLines to base controller.
controller.php:
function myThreeLines(){
$towns = Town::all();
return Redirect::to('home')
->with('towns', $towns);
}
class TestController extend Controller{
function index(){
return $this->myThreeLines();
}
}

Laravel policies - passing the class as a variable to $user->can() method doesn't work

I have a route with dynamic model recognition. In other words, I take the desired model as an argument and use it in the controller. I have complex authorization in my app and I need to pass the model class name as a variable to the $user->can() method for using policies, but for some reason it doesn't work. Here's my code:
Policy:
public function view($user, Model $model) {
return $user->model_id == $model_id;
}
public function create($user) {
return $user->isAdmin();
}
Controller:
public function createModel($model) {
$model_class = $model . '::class';
if (Auth::user()->can('create', $model_class)) {
return $model_class::create();
}
return 'invalid_permissions';
}
If I hardcode the model class name it works. For example, if my model is 'Car' and in the controller I put:
if (Auth::user()->can('create', Car::class)) {
Anybody got any ideas why this is so and how to fix it? I hope that it's possible because I would have to change my whole concept if it isn't.
*Note: this is example code, not my actuall classes

Resources