I have this specific situation, my trait has a method and my class has a method, both with same name.
I need to use both methods (the one from the trait and the class) Inside that class which contains that same method
namespace Some\Namespace;
use Some\Other\Namespace\TestTrait;
class TestClass {
use TestTrait;
public function index()
{
// should call the method from the class $this->getId();
// should also call the method from the trait $this->getId();
}
private function getId()
{
// some code
}
}
And in seperate defined Trait:
trait TestTrait
{
private function getId ()
{
// does something
}
}
Please note this is not pasted code, I might have some typos :P
Use trait Conflict Resolution
namespace Some\Namespace;
use Some\Other\Namespace\TestTrait;
class TestClass {
use TestTrait {
getId as traitGetId;
}
public function index()
{
$this->getId();
$this->traitGetId();
}
private function getId()
{
// some code
}
}
Related
I have an abstract base class implements the laravel queueable trait:
abstract class BaseJob {
use Queueable;
}
Queueable defines $queue:
trait Queueable
{
public $queue;
}
In the actual job it is now possible to defined the classname as the queue name:
class SpecificJob extends BaseJob {
public $queue = self::class;
public function __construct($someParameter) {
// they all have custom constructors so they would overwrite the BaseJob constructor and I would also like to avoid calling parent::__construct;
}
}
Is there some way to do this in the baseclass so it is not required to do it in every class that extends from it?
I thought of something like this:
abstract class BaseJob {
use Queueable;
public $queue = static::class;
}
However this is not possible:
'static::' is not allowed in compile-time constants
You can use late static bindings in the Queueable trait to get the implementing class name if you use a getter rather than a public property.
trait Queueable
{
public function getQueue()
{
return static::class;
}
}
abstract class BaseJob {
use Queueable;
}
class SpecificJob extends BaseJob {
public function __construct($someParameter) {
// they all have custom constructors so they would overwrite the BaseJob constructor and I would also like to avoid calling parent::__construct;
}
}
$mySpecificJob = new SpecificJob('wombats');
assert($mySpecificJob->getQueue() == 'SpecificJob', 'We expect the trait to return the class name of the SpecificJob instance');
echo $mySpecificJob->getQueue().PHP_EOL;
The convention of relying on the implementing class name for the queue name give me hives, personally, I would want to be explicit when naming the queues. From your example, I don't see a reason to use a trait, you can do everything you need to do in the base class if you require sub classes to implement a method that returns the queue name. You could even use the class name by returning the value of static::class if you want.
abstract class BaseJob
{
public abstract function getQueue(): string;
}
class SpecificJob extends BaseJob
{
public function __construct($someParameter)
{
// they all have custom constructors so they would overwrite the BaseJob constructor and I would also like to avoid calling parent::__construct;
}
public function getQueue(): string
{
return self::class;
}
}
class CustomNameJob extends BaseJob
{
private const _queueName = 'custom_queue';
public function __construct($someParameter)
{
// they all have custom constructors so they would overwrite the BaseJob constructor and I would also like to avoid calling parent::__construct;
}
public function getQueue(): string
{
return self::_queueName;
}
}
$mySpecificJob = new SpecificJob('wombats');
assert($mySpecificJob->getQueue() == 'SpecificJob', 'We expect the the class name of the SpecificJob instance');
echo $mySpecificJob->getQueue().PHP_EOL;
$myCustomNameJob = new CustomNameJob('wombats');
assert($myCustomNameJob->getQueue() == 'custom_queue', 'We expect the queue name specified in the CustomNameJob class');
echo $myCustomNameJob->getQueue().PHP_EOL;
I have an abstract class, in which I want to call method, from a class that is declared in the child (extending) class. An example looks like this:
The abstract class:
abstract class NumberGenerator
{
protected function generate($input){
return MyClass::MyMethod($input);
}
}
My child/extending class:
use TomsFolder\MyClass;
use MyFolder\NumberGenerator;
class TomsNumberGenerator extends NumberGenerator
{
public function generate(string $applicantId): string
{
return $this->generate();
}
}
Another child/extending class:
use DavesFolder\MyClass;
use MyFolder\NumberGenerator;
class DavesNumberGenerator extends NumberGenerator
{
public function generate(string $applicantId): string
{
return $this->generate();
}
}
So I want to call MyClass::MyMethod in NumberGenerator. However it is only imported in TomsNumberGenerator.
The reason I want to do it like is because, I have classes like DavesNumberGenerator which calls a different MyClass.
When I try this, I get 'MyClass is not found in NumberGenerator'. Is there any way to make this work?
Try putting the namespace use statement before the actual class:
NumberGenerator.php
use MyFolder\MyClass;
abstract class NumberGenerator
{
protected function generate($input){
return MyClass::MyMethod($input);
}
}
EDIT
Try this:
NumberGenerator.php
abstract class NumberGenerator
{
protected function generate($class_name, $input){
return call_user_func($class_name . '::MyMethod', $input);
}
}
TomsNumberGenerator.php
use TomsFolder\MyClass;
use MyFolder\NumberGenerator;
class TomsNumberGenerator extends NumberGenerator
{
public function generate(string $applicantId): string
{
return $this->generate(get_class(new MyClass()), $applicantId);
}
}
You have to use interface for this.
You can do the following
Create MyClassInterface
interface MyClassInterface {
public function MyMethod();
}
Implement this interface in some classes
class MyClass1 implements MyClassInterface {
public function MyMethod() {
// implementation
}
}
class MyClass2 implements MyClassInterface {
public function MyMethod() {
// implementation 2
}
}
Add abstract method to NumberGenerator
abstract class NumberGenerator {
abstract protected function GetMyClass(): MyClassInterface;
protected function generate($input){
return $this->GetMyClass()->MyMethod($input);
}
}
Implement GetMyClass function inside child classes
class TomsNumberGenerator extends NumberGenerator
{
protected function GetMyClass(): MyClassInterface {
return new MyClass1();
}
}
class DavesNumberGenerator extends NumberGenerator
{
protected function GetMyClass(): MyClassInterface {
return new MyClass2();
}
}
PS If you want to use static, you can change abstract inside NumberGenerator class, to change string. In this case, your generate will look like this:
protected function generate($input){
return call_user_func($this->GetMyClass() . '::MyMethod', [$input]);
}
Have a look at the following trait:
trait PrimaryModelRest {
use RestController;
protected $primaryModel;
public function __construct() {
$mc = $this->getPrimaryModelClass();
try {
$this->primaryModel = new $mc();
if(!($this->primaryModel instanceof Model)) {
throw new ClassNotFoundException("Primary Model fatal exception: The given Class is not an instance of Illuminate\Database\Eloquent\Model");
}
} catch (Exception $e) {
throw new WrongImplementationException("Primary Model Exception: Class not found.");
}
}
/**
* #return string: Classname of the primary model.
*/
public abstract function getPrimaryModelClass();
// various functions here
}
As you can see the trait makes sure that the using class holds a certain model instance and it implements certain methods. This works as long as the implementing class does not override the constructor.
So here is my question: I want to make sure that either the constructor is called or a better solution, such that I can instantiate this model on initialization.
Please make in answer which respects Multiple inheritance as well es Multi-Level inheritance.
I think you are trying to make the trait do a job it is not designed for.
Traits are not a form of multiple inheritance, but rather "horizontal reuse" - they're often described as "compiler-assisted copy-and-paste". As such, the job of a trait is to provide some code, so that you don't have to copy it into the class manually. The only relationship it has is with the class where the use statement occurs, where the code is "pasted". To aid in this role, it can make some basic requirements of that target class, but after that, the trait takes no part in inheritance.
In your example, you are concerned that a sub-class might try to access $primaryModel without running the constructor code which initialises it, and you are trying to use the trait to enforce that; but this is not actually the trait's responsibility.
The following definitions of class Sub are completely equivalent:
trait Test {
public function foo() {
echo 'Hello, World!';
}
}
class ParentWithTrait {
use Test;
}
class Sub inherits ParentWithTrait {
}
vs:
class ParentWithMethodDefinition {
public function foo() {
echo 'Hello, World!';
}
}
class Sub inherits ParentWithMethodDefinition {
}
In either case, class Sub could have its own definition of foo(), and by-pass the logic you'd written in the parent class.
The only contract that can prevent that is the final keyword, which in your case would mean marking your constructor as final. You can then provide an extension point that can be overridden for sub-classes to add their own initialisation:
class Base {
final public function __construct() {
important_things(); // Always run this!
$this->onConstruct(); // Extension point
}
protected function onConstruct() {
// empty default definition
}
}
class Sub {
protected function onConstruct() {
stuff_for_sub(); // Runs after mandatory important_things()
}
}
A trait can also mark its constructor as final, but this is part of the code being pasted, not a requirement on the class using the trait. You could actually use a trait with a constructor, but then write a new constructor as well, and it would mask the trait's version completely:
trait Test {
final public function __construct() {
echo "Trait Constructor";
}
}
class Noisy {
use Test;
}
class Silent {
use Test;
public function __construct() {
// Nothing
}
}
As far as the trait is concerned, this is like buying a bottle of beer and pouring it down the sink: you asked for its code and didn't use it, but that's your problem.
Crucially, though, you can also alias the methods of the trait, creating a new method with the same code but a different name and/or a different visibility. This means you can mix in code from traits which declare constructors, and use that code in a more complex constructor, or somewhere else in the class altogether.
The target class might also use the "final + hook" pattern:
trait TestOne {
final public function __construct() {
echo "Trait TestOne Constructor\n";
}
}
trait TestTwo {
final public function __construct() {
echo "Trait TestTwo Constructor\n";
}
}
class Mixed {
final public function __construct() {
echo "Beginning\n";
$this->testOneConstructor();
echo "Middle\n";
$this->testTwoConstructor();
echo "After Traits\n";
$this->onConstruct();
echo "After Sub-Class Hook\n";
}
use TestOne { __construct as private testOneConstructor; }
use TestTwo { __construct as private testTwoConstructor; }
protected function onConstruct() {
echo "Default hook\n";
}
}
class ChildOfMixed extends Mixed {
protected function onConstruct() {
echo "Child hook\n";
}
}
The trait hasn't forced the Mixed class to implement this pattern, but it has enabled it, in keeping with its purpose of facilitating code reuse.
Interestingly, the below code doesn't work, because the as keyword adds an alias, rather than renaming the normal method, so this ends up trying to override the final constructor from Mixed:
class ChildOfMixed extends Mixed {
use TestTwo { __construct as private testTwoConstructor; }
protected function onConstruct() {
$this->testTwoConstructor();
echo "Child hook\n";
}
}
Use a base class, this will let you handle the trait as a parent.
<?php
trait StorageTrait
{
public function __construct()
{
echo "Storage Trait";
}
}
class StorageAttempt
{
use StorageTrait;
public function __construct()
{
parent::__construct();
echo " - Storage Attempt";
}
}
abstract class StorageBase
{
use StorageTrait;
}
class MyStorage extends StorageBase
{
public function __construct()
{
parent::__construct();
echo ' - My Storage';
}
}
new StorageAttempt(); // won't work - will trigger error
new MyStorage(); // will display "Storage Trait - My Storage"
Also if you are using traits you can also work with properties and getters & setters.
Example: A Storage trait involves that a Storage Engine will be used. You can add the storageEngine property and its getters and setters. (with or without Type Hinting)
interface StorageEngineInterface{}
trait StorageTrait
{
/**
* #var StorageEngineInterface
*/
protected $storageEngine;
/**
* #return StorageEngineInterface
*/
public function getStorageEngine(): StorageEngineInterface
{
return $this->storageEngine;
}
/**
* #param StorageEngineInterface $storageEngine
*/
public function setStorageEngine(StorageEngineInterface $storageEngine)
{
$this->storageEngine = $storageEngine;
return $this;
}
}
Note: this is just an explanation so you can better understand how Traits work
UPDATE
To avoid conflict you can use aliases for trait methods. This way you can use both constructors (from trait and from extended class) you can do the following
class DifferentStorage
{
public function __construct()
{
echo ' diff ';
}
}
class MyDifferentStorage extends DifferentStorage
{
use StorageTrait {
StorageTrait::__construct as otherConstructor;
}
public function __construct()
{
parent::__construct();
self::otherConstructor();
}
}
You could use the interface injection pattern: implement an interface iPrimaryModelRest into the same class that uses the trait PrimaryModelRest:
interface iPrimaryModelRest {
public function init();
public abstract function getPrimaryModelClass();
}
The class that uses the trait woud look like this:
class cMyClass implements iPrimaryModelRest {
use PrimaryModelRest;
}
Then, whenever the class is instantiated (not only autoloaded) you could call a special factory-like initialisation function like this:
class cMyApp {
public function start() {
/** #var cMyClass $oClass */ // enlighten IDE
$oClass = $this->init(new cMyClass);
}
public function init($oClass) {
if ($oClass instanceof iPrimaryModelRest) {$oClass->init();}
if ($oClass instanceof whateverinterface) {
// pass optional stuff, like database connection
}
}
}
The interface is used to determine the capabilities of the class, and sets data/runs corresponding functions. If I'm not mistaken then this pattern is called a Service Locator.
I needed a trait for database connection. To avoid using the __construct in a trait, I've used a magic getter instead:
trait WithDatabaseConnection
{
public function __get(string $name)
{
if ($name === 'pdo') {
return App::make(\PDO::class);
}
trigger_error("Property $name does not exist.");
return null;
}
}
class Foo {
use WithDatabaseConnection;
public function save() {
$this->pdo->query('...');
}
}
trait TestTrait
{
public function matches()
{
}
}
class TestClassOne
{
public static function matches()
{
}
}
class TestClassTwo extends TestClassOne
{
use TestTrait {
matches as alternativeMatches;
}
public function run()
{
$this->alternativeMatches();
}
}
When calling the method run as seen above it will return the error :Fatal error: Cannot make static method matches non static
The classes are not written by me and the trait I have used in multiple other locations. A hell if I have to rewrite the trait .. or have to make a new one , just to make it work with this class.
Is there a way to make it work ?
Cheers
Well I made it work with a bit of a hack ... but I do not like it...
Someone has a nicer way of doing this ?
class TestClassTwo extends TestClassOne
{
use TestTrait {
matches as alternativeMatches;
}
public function run()
{
$this->alternativeMatches();
}
public static function matches()
{
parent::matches();
}
}
Im talking about this function:
function testMeSomehow ($id, Flag $flags)
{
$flags::NAME;
}
its parameter object:
abstract class Flag
{
abstract function method1();
abstract function method2();
.
.
.
abstract function method999();
}
how to mock this Flag class? It has tons of abstract methods, should I create all of them with empty body? And what if this class changes? I also have to add a NAME constant to it
You can mock it with test doubles like you would do for any other class, see https://phpunit.de/manual/current/en/test-doubles.html
This could be an example test:
class TargetClass
{
public function testMeSomehow($id, Flag $flag)
{
return $flag->method1();
}
}
class TargetClassTest extends PHPUnit_Framework_TestCase
{
public function testSomething()
{
$mock = $this->getMock('Flag');
$mock->expects($this->once())
->method('method1')
->willReturn('methodResult');
$targetClass = new TargetClass();
$this->assertEquals('methodResult', $classToTest->testMeSomehow(1, $mock));
}
}
You can specify the methods that you want to replace by the mock as the 2nd parameter of $this->getMock(). Because we don't specify anything at all, it will replace all methods and thus won't bother about the abstract methods.
Edit: added an example to access constants:
class ClassToTest
{
public function testMeSomehow($id, Flag $flag)
{
return $flag::NAME;
}
}
class FlagTest extends PHPUnit_Framework_TestCase
{
public function testStuff()
{
$mock = $this->getMock('Flag');
$classToTest = new ClassToTest();
$this->assertEquals('Flag name', $classToTest->testMeSomehow(1, $mock));
}
}
If you want a specific value for that constant in your tests, I suggest to make a child class with this constant and use that for mocking.
Edit: added an example with a workaround to specify dynamic constant values.
class Flag
{
const NAME = 'Flag name';
public function getName()
{
return static::NAME;
}
}
class TargetClass
{
public function testMeSomehow($id, Flag $flag)
{
return $flag->getName();
}
}
class TargetClassTest extends PHPUnit_Framework_TestCase
{
public function testSomething()
{
$mock = $this->getMock('Flag');
$mock->expects($this->any())
->method('getName')
->willReturn('New flag name');
$targetClass = new TargetClass();
$this->assertEquals('New flag name', $classToTest->testMeSomehow(1, $mock));
}
}
However, this compels you to use the getName() method everywhere, so I personally prefer the previous suggestion: mocking a child class that has the changed value.