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.
Related
I am trying to separate functionalities into two classes and I want to Inject one class into another. However, it seems that Laravel can't recognize the second class and it is always null.
namespace App\Services;
use App\Models\Image;
use App\Models\Offer;
class ImagesService {
public function __construct() {
}
function saveImages(iterable $images, Offer $offer): array {
// ... code here !
}
}
And the class to inject in is:
namespace App\Services;
use App\Models\Action;
use App\Models\Offer;
use App\Models\OfferOption;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class OffersService {
protected $imagesService;
function __constructor(ImagesService $imgService) {
$this->imagesService = $imgService; //Doesn't work ! $imagesService is always null!
}
function doSomething() {
$this->imagesService->saveImages(....) // Call to a member function saveImages() on null at
}
}
there are several ways to access and use properties from one class to another class.
Create an instance of the class you want to use in the class you want to use.
Use the class you want to use as Trait and call it use in the other class.
Extend the class you want to use to the class you want to use.
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.
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.
I'm trying to use traits in CodeIgniter. I put the Trait in the libraries directory and then loaded the library in the controller. Then I used the trait in the model, but it didn't work and I received the following error
Trait file (in library):
trait DataTablesTrait{
public function query(){
$this->db->select('*');
$this->db->limit(10, 1);
return $this->db->get('my_table');
}
}
?>
Controller:
class myController extends CI_Controller {
public function __construct(){
parent::__construct();
$this->load->library('DataTablesTrait');
$this->load->model('ABC_Model');
}
}
Model:
class ABC_Model extends CI_Model {
use DataTablesTrait;
function callQuery(){
$this->query();
}
}
I got this error:
Non-existent class: DataTablesTrait
Please advise
CodeIgniter (CI) isn't trait or namespace friendly but they can be used. It requires working around CI a bit though.
The main problem, the CI thing you have to work around, is the call
$this->load->library('DataTablesTrait');
CI will find this file but load->library will try to instantiate the trait which fails because it is not possible to instantiate a Trait on its own.
Replace the line above with
include APPPATH.'/libraries/DataTablesTrait.php';
You should be free of the error with that. But you're not going to get results because callQuery() does not return anything. To round out the test have callQuery() return the CI_DB_result object the Trait should produce
public function callQuery()
{
return $this->query();
}
Add an index function to the controller so we can dump the output
public function index()
{
$DB_result = $this->ABC_Model->callQuery();
var_dump($DB_result->result());
}
This should produce the expected output - assuming that 'my_table' has data :)
Worth saying that traits are now fully supported in Codeigniter 4 and can be implemented as you would expect using namespaces etc.
// TRAIT
namespace App\Controllers\traits;
trait YourTraitName {
}
// CONTROLLER
use App\Controllers\traits\YourTraitName;
class Admin extends BaseController {
use YourTraitName;
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.