Laravel use Trait methods in another Trait - php

Normally I have a question about something not working, now I have a question about something that IS working, I am just confused as to why. This is the structure that I have in Laravel:
ExampleController
use App\Http\Traits\Trait1;
use App\Http\Traits\Trait2;
ExampleController extends Controller {
use Trait1, Trait2;
public function index()
{
// I can use methods from Trait1 and Trait2 here, works fine
}
}
Trait1
namespace App\Http\Traits;
trait Trait1 {
exampleMethodTrait1()
{
}
}
Trait2
namespace App\Http\Traits;
trait Trait2 {
$test = $this->exampleMethodTrait1();
}
Calling a method defined in Trait1 from Trait2 actually works, while I have not added use App\Http\Traits\Trait1; in Trait2. Is that because they are both loaded in the controller?

Okay, Let me put same code and explain you why it is working.
Trait1
<?php
namespace App\Http\Traits;
trait Trait1 {
public function exampleMethodTrait1()
{
echo 'okay';
}
}
?>
Trait 2
<?php
namespace App\Http\Traits;
trait Trait2 {
public function bar() {
var_dump(get_class($this));
$test = $this->exampleMethodTrait1();
}
}
?>
MyController
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Traits\Trait1;
use App\Http\Traits\Trait2;
class MyController extends Controller
{
use Trait1, Trait2;
/**
* Show the application dashboard.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
$this->bar();
}
}
Now, if you will notice in Trait 2, var_dump(get_class($this)); $this is instance of MyController and not instance of trait 2, that is how it is working and it is expected behavior.
Now if you want to know if you can use one trait in side another
YES
You can do like
TaraitA
Trait A {
}
TraitB
Trait B {
use A;
}
And it will work fine.

Yes, they are both loaded in your controller as a part of it therefore they have access between them also controller methods
See the example 4
https://www.php.net/manual/en/language.oop5.traits.php
Regards

I think your confusion comes from believing that the $this inside a trait corresponds to the trait itself. But it is not.
Traits are nothing by themselves: they exists only in the context of a real class, as a helper to copy-paste methods around but not visually polluting your actual classes.
The $this you use to call exampleMethodTrait1 is not an instance of Trait2 (nor Trait1) but an instance of ExampleController, that has copied the methods over from the traits.
This doesn't happen only with traits, though, but also with parent classes in the hierarchy:
Example
abstract class Base {} // First level of inheritance
class Building extends Base {} // Second level of inheritance
class House extends Building {} // Last level of inheritance
$this (and static) always corresponds to an instance of the most concrete class of the hierarchy (the last level of inheritance).
self instead refers to the actual class instance (the same level of inheritance where the method is defined). Still never a trait, they cannot be instantiated by themselves.
the traits are not part of the hierarchy, but blindly pasted where you use them.

Related

Use Imported Classes From Parent Class In a PHP Trait Laravel 5.5

I'm currently working on a PHP trait thay will help me to reuse code in some class controllers that I have using Laravel framework.
I wanted to make the trait methods as dynamic as I could but when trying to access to a class that my parent class imported, I get a Class not found exception.
My class controller is as follows:
namespace App\Http\Controllers\Admin;
use App\Models\ {
Curso,
Leccion,
Diapositiva,
ImagenDiapositiva
};
use App\Traits\TestTrait;
class DiapositivasController extends Controller{
use TestTrait;
public function addRecord(Request $request){
$request->class_name = 'ImagenDiapositiva';
$this->addImage($request);
}
}
My Trait:
namespace App\Traits;
trait TestTrait{
public function addImage($request){
$class_name = $request->class_name;
$diapositiva = new $class_name;
//extra code
}
}
So my doubt is, do I have to include the model classes I want to use inside my Trait again or am I doing something else wrong?
if you use new with a variable class name, you have to use the fully qualified class name. I'm guessing new $class_name is the root cause of the issue here, since $class_name would have to be something like: 'App\Models\ImagenDiapositiva' or whatever the full namespace is. Just have to change the call $request->class_name = 'ImagenDiapositiva'; to reflect the full name of the class.

PHP Laravel 5 : Trait not found

I am having an issue utilizing traits in a laravel enviroment.
I have attempting to access the trait from wtihin a model in laravel and am getting trait not found error. Below is my trait, this is stored in the app\Traits folder and is named testTrait.php
<?php
namespace App\Traits;
trait test{
public function printTest()
{
$test = 'test';
return $test;
}
}
This is the model I am using to try and access it, this is stored in the app\Models folder and otherwise works fine without attempting to use the trait
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Traits\testTrait;
class Home extends Model
{
use testTrait;
public function someMethod()
{
/*
something here
*/
}
}
I assume it might have something to do with namespacing and the fact that I have to assign the App\Models namespace but even if I change or remove this it doesn't work.
Your file may be named testTrait.php, but you've called the trait in that file plain old test.
trait test { ... }
Rename it to testTrait in the testTrait.php file.
trait testTrait { ... }
You have defined your trait as trait test{ but in your model you are using wrong trait name use testTrait;
change it to
use test;

How to call a trait method with alias

I'm trying to place a trait inside a class called Page. I also need to rename a trait function so that it doesn't clash with an existing class function. I thought I did all this successfully however I get an error that points to the wrong location?!
Call to undefined function App\Pages\Models\myTraitDefaultScope()
I've also tried: MyTrait\defaultScope($query) instead of trying to rename the conflicting function. But I then get the following error:
Call to undefined function App\MyTrait\defaultScope()
Below is the trait and class contained in separate files.
<?php
namespace App;
use Illuminate\Support\Facades\Auth;
trait MyTrait{
public function defaultScope($query){
return $query->where('active', '1')
}
}
.
<?php namespace Modules\Pages\Models;
use Illuminate\Database\Eloquent\Model;
use App\MyTrait;
class Page extends Model {
use MyTrait{
MyTrait::defaultScope as myTraitDefaultScope;
}
public function defaultScope($query){
return myTraitDefaultScope($query);
}
}
I'm not all that awesome at this so please don't shoot if I've got something badly wrong :)
When you 'use' a trait in your class, the class inherits all the methods and properties of the trait, like if it was extending an abstract class or an interface
So, this method of MyTrait:
public function defaultScope($query){
return $query->where('active', '1')
}
will be inherited by your Page class
As you have aliased this method as: myTraitDefaultScope, to call the method you should call it in the same way you would call every other method of the Page class:
public function defaultScope($query){
//call the method of the class
return $this->myTraitDefaultScope($query);
}
As you're using trait. So it points to the current or parent class. Thus, calling any method should be like $this->method($params); syntax.

How to access class members in traits (or get a similar behaviour)?

This is a follow-up to my previous question about resolving the diamond issue in php.
As I state in that question, I resolve my problem by using traits and passing the instance of the class to the method of the trait. Such as:
trait SecurityTrait
{
public function beforeExecuteRouteTrait($controller, Dispatcher $dispatcher)
{
// Do something that makes use of methods/members of the controller
}
}
class AppController extends Controller
{
use SecurityTrait;
public function beforeExecuteRoute(Dispatcher $dispatcher)
{
return $this->beforeExecuteRouteTrait($this, $dispatcher);
}
}
However, I am still uncomfortable with this as I don't think this is how traits are really supposed to be used. In my reading I haven't found any way in which to access class members in traits (make $this inside a trait refer to the class using it). Is this possible? Or is there another way to implement a similar behaviour?
After reading some of the answers...
Previously I thought I had received errors when using $this->... inside the trait and this led me to believe the trait could not access anything to do with the underlying class. After reading the answers I tried altering my code to use $this->... inside a trait again and it works - which means a typo several weeks ago has given me far too much headache...
The example given previously now looks like this
trait SecurityTrait
{
public function beforeExecuteRoute(Dispatcher $dispatcher)
{
// Do something that makes use of methods/members of the controller
}
}
class AppController extends Controller
{
use SecurityTrait;
}
Much cleaner and more easily understandable but provides the same functionality.
If you use a trait inside a class then that trait has full access to all class's members and vice versa - you can call private trait methods from the class itself.
Think of traits as code that literally gets copy/pasted into the class body.
For example:
trait Helper
{
public function getName()
{
return $this->name;
}
private function getClassName()
{
return get_class($this);
}
}
class Example
{
use Helper;
private $name = 'example';
public function callPrivateMethod()
{
// call a private method on a trait
return $this->getClassName();
}
}
$e = new Example();
print $e->getName(); // results in "example"
print $e->callPrivateMethod(); // results in "Example"
In my view referencing classes in traits is not the best way to use them but there's nothing stopping anyone from doing it.
No, that's exactly what Traits are for. Your class already extends a class so you can't inherit the methods and variables of any other classes.
Think of a Trait like copy/paste for code execution. When a class includes a Trait, it's just as if you had written all that code into the class itself.

How to override a trait's method in abstract class?

I'm stuck into a problem with traits I can't solve on my own.
I have classes extending an abstract class (in my case these are several controller classes and an abstract class Controller, the used framework won't be important here, since this is a general PHP question…) that uses traits. I'd like to override a method defined in one of the traits. This only works as long as I define the method in my sub-classes but not in my abstract class.
So, this one works perfectly:
class MyController extends Controller
{
use AnyTrait;
public function anyMethodFromAnyTrait()
{
// override AnyTrait::anyMethodFromAnyTrait()
}
}
I also know how to call the anyMethodFromAnyTrait method from AnyTrait by using as.
class MyController extends Controller
{
use AnyTrait { AnyTrait::anyMethodFromAnyTrait as method }
public function anyMethodFromAnyTrait()
{
// invoke AnyTrait::anyMethodFromAnyTrait()
$this->method();
}
}
Both work like a charm.
But my problem is a bit different.
When using the trait and defining the method in my abstract class I am not able to override the trait's method.
Assume the following controller class:
class MyController extends Controller
{
public function anyAction()
{
// let's see what happens…
$this->anyMethodFromAnyTrait();
}
}
…and the abstract one that's extended by MyController:
abstract class Controller
{
use AnyTrait
public function anyMethodFromAnyTrait()
{
// do something different than AnyTrait
}
}
…And this is what's not working at all. Whenever I call $this->anyMethodFromAnyTrait() within MyController the trait's method as implememented in AnyTrait will be invoked. The same named method in my abstract Controller will be ignored.
Therefore I only can override a trait's method in a concrete sub-class but not in an abstract class that is extended by that sub-class.
So the method definitions in traits get a higher priority by PHP than the same method definitions in abstract classes.
Do you know any workaround for that behaviour?
One workaround would be to use the traits ONLY in the subclasses.
PHP always prefers the trait methods over the "local" ones.
The reason why it works in subclasses is, that the trait method of the superclass is extended, not the trait usage itself.

Categories