I am using Laravel (PHP). I want to refactor below code to ask class A and class B to use the same funsth(). funSth() is repeated in two class while the only difference is the model.
Is there a way I can simplify to below? Is that possible to refactor it into second code?
Current:
class A {
public function funSth(){
Models\Profile::create();
}
public function main(){
$this->funA();
}
}
class B {
public function funSth(){
Models\Location::create();
}
public function main(){
$this->funB();
}
}
Expected:
class A {
public function main(){
(new Y)->funSth();
}
}
class B {
public function main(){
(new Y)->funSth();
}
}
class Y {
public function funSth(){
Models\AnyModel::create();
}
}
One way you could do this is to make a Trait. Traits essentially allow you to use the same code in multiple classes.
Base on the code in your question, your trait would look something like:
trait Y
{
public function funSth()
{
Models\AnyModel::create();
}
}
Don't forget to add the correct namespace for the file.
Then in your other classes you would have the use the use keyword inside the class body:
class A
{
use Y;
public function main()
{
$this->funA();
}
}
If you're trait is in a different namespace then you'll need to either give the full class name with the namespace or import the trait into your class.
After researching for a while. Here is my solution.
use Illuminate\Database\Eloquent\Model;
trait Y
{
protected function create(Model $model, array $attributes){
model->create($attributes);
}
}
Therefore, for Class A and Class B:
class A
{
use Y;
public function main($data){
$this->create((new Models\ModelAA), $data)
}
}
class B
{
use Y;
public function main($data){
$this->create((new Models\ModelBB), $data)
}
}
The basic idea is to use the abstract class Illuminate\Database\Eloquent\Model in trait Y and initialize model before it.
Related
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]);
}
I have situation like this. I have some 3rd party trait (I don't want to test) and I have my trait that uses this trait and in some case runs 3rd party trait method (in below example I always run it).
When I have code like this:
use Mockery;
use PHPUnit\Framework\TestCase;
class SampleTest extends TestCase
{
/** #test */
public function it_runs_parent_method_alternative()
{
$class = Mockery::mock(B::class)->makePartial();
$class->shouldReceive('fooX')->once();
$this->assertSame('bar', $class->foo());
}
protected function tearDown()
{
Mockery::close();
}
}
trait X {
function foo() {
$this->something->complex3rdpartyStuff();
}
}
trait Y2 {
function foo() {
$this->fooX();
return 'bar';
}
}
class B {
use Y2, X {
Y2::foo insteadof X;
X::foo as fooX;
}
}
it will work fine however I don't want code to be organized like this. In above code in class I use both traits but in code I want to test in fact trait uses other trait as mentioned at the beginning.
However when I have code like this:
<?php
use Mockery;
use PHPUnit\Framework\TestCase;
class SampleTest extends TestCase
{
/** #test */
public function it_runs_parent_method()
{
$class = Mockery::mock(A::class)->makePartial();
$class->shouldReceive('fooX')->once();
$this->assertSame('bar', $class->foo());
}
protected function tearDown()
{
Mockery::close();
}
}
trait X {
function foo() {
$this->something->complex3rdpartyStuff();
}
}
trait Y {
use X {
foo as fooX;
}
function foo() {
$this->fooX();
return 'bar';
}
}
class A {
use Y;
}
I'm getting:
undefined property $something
so it seems Mockery is not mocking in this case X::foo method any more. Is there are way to make possible to write such tests with code organized like this?
So far it is not possible to mock deeper aliased methods. You can proxy aliased method call using local method and allowing mocking protected methods.
Check code below
use Mockery;
use PHPUnit\Framework\TestCase;
class SampleTest extends TestCase
{
/** #test */
public function it_runs_parent_method()
{
$mock = Mockery::mock(A::class)->shouldAllowMockingProtectedMethods()->makePartial();
$mock->shouldReceive('proxyTraitCall')->once();
$this->assertSame('bar', $mock->foo());
}
protected function tearDown()
{
Mockery::close();
}
}
trait X {
function foo() {
$this->something->complex3rdpartyStuff();
}
}
trait Y {
use X {
foo as fooX;
}
function foo() {
$this->proxyTraitCall();
return 'bar';
}
function proxyTraitCall() {
return $this->fooX();
}
}
If you autoload trait you can try to overload it using Mockery.
/** #test */
public function it_runs_parent_method()
{
$trait = Mockery::mock("overload:" . X::class);
$trait->shouldReceive('foo')->once();
$class = Mockery::mock(A::class)->makePartial();
$this->assertSame('bar', $class->foo());
}
Don't test implementation details. Test it like you use it.
Class user have to know only public interface to use it, why test should be any different?
Fact that one internal method call different one is implementation detail and testing this breaks encapsulation. If someday you will switch from trait to class method without changing class behaviour you will have to modify tests even though class from the outside looks the same.
From Pragmatic Unit Testing by Dave Thomas and Andy Hunt
Most of the time, you should be able to test a class by exercising its
public methods. If there is significant functionality that is hidden
behind private or protected access, that might be a warning sign that
there's another class in there struggling to get out.
I have function like this:
class Bar{
public function a():Foo{
.
.
.
}
}
now I am trying to create a mock for the class Bar with php unit test
$mockedBar = $this->getMockBuilder(Bar::class)
->getMock()
->method('a')
->willReturn(new FakeFoo());
but when I am calling method a I am getting an error that method a return type must be instance of Foo not Mocked_blahblah.
unfortunately class Bar don't use any interface and the system is very big and I can't create an interface cause it make huge refactor in my codes;
is there any way to disable return type of function a in mocked object?
I am useing php7.2 and phpunit 6.0.13.
Here is a real scenario:
class A
{
public function b():B
{
echo "i am from class A function b";
}
}
class B
{
}
class FakeB
{
}
class ATest extends TestCase
{
public function testSayHi(){
$mockedA = $this->getMockBuilder(A::class)
->getMock();
$mockedA->method('b')->willReturn(new FakeB());
$mockedA->b();
}
}
You can't disable return types. Perhaps you could try to do it with some kind of a hackish error handler, but it's a crazy thing to do.
Good news is that you're not trying to do anything unusual and your tests can be fixed.
Firstly, you need to assign the result of getMock to a variable. Next, you can define your test double:
class MyTest extends TestCase
{
public function testIt()
{
$mockedBar = $this->getMockBuilder(Bar::class)->getMock();
$mockedBar
->method('a')
->willReturn(new FakeFoo());
$this->assertInstanceOf(Foo::class, $mockedBar->a());
}
}
This will only work if FakeFoo is of type of Foo, for example extends it:
class FakeFoo extends Foo
{
// override any Foo methods you'd like to fake
}
You don't need to create a Fake yourself, you can use PHPUnit to create a dummy:
class MyTest extends TestCase
{
public function testIt()
{
$mockedBar = $this->createMock(Bar::class);
$mockedBar
->method('a')
->willReturn($this->createMock(Foo::class));
$this->assertInstanceOf(Foo::class, $mockedBar->a());
}
}
To fix your second example:
class A
{
public function b():B
{
echo "i am from class A function b";
}
}
class B
{
}
class FakeB extends B
{
}
class ATest extends TestCase
{
public function testSayHi(){
$mockedA = $this->getMockBuilder(A::class)->getMock();
$mockedA->method('b')->willReturn(new FakeB());
$mockedA->b();
}
}
Or, instead of using a fake let phpunit handle it:
class ATest extends TestCase
{
public function testSayHi(){
$mockedA = $this->getMockBuilder(A::class)->getMock();
$dummyB = $this->createMock(B::class);
$mockedA->method('b')->willReturn($dummyB);
$mockedA->b();
}
}
Overwrite namespace usage in extended class
Is it possible to overwrite the used namespace of the parent class without rewriting the function in the extended class?
For clarification i write down an example:
i have two classes like this:
namespace one;
class hey
{
public static function say()
{
echo "hey";
}
}
and
namespace two;
class hey
{
public static function say()
{
echo "ho";
}
}
Now i use on of the namespaces in this class:
use one\hey;
class saysomething
{
public static function main()
{
hey::say();
}
}
Now i want to extend the last class:
class extended extends saysomething
{
}
extended::main();
In this class i want to use namespace "two\one" without overwriting the function, is it possible? *f it is, how?
Thank you for your time.
You must define extended::main() within extended class, like this:
use two\hey;
class extended extends saysomething
{
public static function main()
{
hey::say(); // will call two/hey::say method
}
}
Namespaces won't help here.
I think there is a problem in php's OOP implementation.
EDIT: Consider more illustrative example:
abstract class Animal {
public $name;
// public function Communicate(Animal $partner) {} // Works
public abstract function Communicate(Animal $partner); // Gives error
}
class Panda extends Animal {
public function Communicate(Panda $partner) {
echo "Hi {$partner->name} I'm a Panda";
}
}
class Human extends Animal {
public function Communicate(Human $partner) {
echo "Hi {$partner->name} I'm a Human";
}
}
$john = new Human(); $john->name = 'John';
$mary = new Human(); $mary->name = 'Mary';
$john->Communicate($mary); // should be ok
$zuzi = new Panda(); $zuzi->name = 'Zuzi';
$zuzi->Communicate($john); // should give error
The problem is that when Animal::Communicate is an abstract method, php tells that the following methods are illegal:
"public function Communicate(Panda $partner)"
"public function Communicate(Human $partner)"
but when Animal::Communicate is non-abstract but has zero-implementation Php thinks that these methods are legal. So in my opinion it's not right because we are doing override in both cases, and these both cases are equal, so it seems like it's a bug...
Older part of the post:
Please consider the following code:
Framework.php
namespace A
{
class Component { ... }
abstract class Decorator {
public abstract function Decorate(\A\Component $component);
}
}
Implementation.php
namespace B
{
class MyComponent extends \A\Component { ... }
}
MyDecorator.php
namespace A
{
class MyDecorator extends Decorator {
public function Decorate(\B\MyComponent $component) { ... }
}
}
The following code gives error in MyDecorator.php telling
Fatal error: Declaration of MyDecorator::Decorate() must be compatible with that of A\Decorator::Decorate() in MyDecorator.php on line ...
But when I change the Framework.php::Decorator class to the following implementation:
abstract class Decorator {
public function Decorate(\A\Component $component) {}
}
the problem disappears.
I'm not sure (haven't tested it ;), but you declare this abstract function:
public abstract function Decorate(\A\Component $component);
So you should implement this EXACTLY like that. But you did this:
public function Decorate(\B\MyComponent $component) { ... }
That's not the same. Could you try to change that to \A\Component?
To all comments: fact of the matter is that this piece of PHP "runs"
namespace A
{
class Component { }
abstract class Decorator {
public abstract function Decorate(\A\Component $component);
}
}
namespace B
{
class MyComponent extends \A\Component { }
}
namespace A
{
class MyDecorator extends Decorator {
public function Decorate(\A\Component $component) {}
}
}
And this doesn't:
<?php
namespace A
{
class Component { }
abstract class Decorator {
public abstract function Decorate(\A\Component $component);
}
}
namespace B
{
class MyComponent extends \A\Component { }
}
namespace A
{
class MyDecorator extends Decorator {
public function Decorate(\B\MyComponent $component) {}
}
}
?>
With this error: PHP Fatal error: Declaration of A\MyDecorator::Decorate() must be compatible with that of A\Decorator::Decorate() in line 18
Now you can discuss all you like about how that should or should not be, but that's the problem with the code.
so, to satisfy my own curiosity: this is illegal too:
<?php
class Component { }
abstract class Decorator {
public abstract function Decorate(Component $component);
}
class MyComponent extends Component { }
class MyDecorator extends Decorator {
public function Decorate(MyComponent $component) {}
}
?>
It's not the namespaces or anything. It just doesn't seem legal.
See http://bugs.php.net/bug.php?id=36601, this issues has been reported as a bug but was rejected because of laziness :D
It has nothing to do with it being abstract. It has to do with the type hinting. The two definitions of the method are not compatible because you explicitly set the argument to be of type \A\Component and then try to overload the method with \B\Component you cant do that because it changes the method signature. Any subsequent declaration of Decorate must use the same type hint as its parent declaration in order for the method signatures to be compatible.
This might assist someone, and am not late.
The best way to handle such is by using an interface.
Consider below;
<?php
interface Componentor{}
class Component implements Componentor { }
abstract class Decorator {
public abstract function Decorate(Componentor $component);
}
class MyComponent extends Component { }
class MyDecorator extends Decorator {
public function Decorate(Componentor $component) {}
}
?>
Usage;
<?php
$c=new Component();
//TODO ....blah blah blah...
$decor=new MyDecorator();
//TODO ....blah blah blah...
$decor->Decorate($c);
?>