Laravel facade __constructor() usage - php

I have made a class. The class's constructor assigns a value to a class property.
Here is the definition:
class myClass
{
private $_value;
function __construct ($input)
{
$this->_value = $input;
}
function echoThat ()
{
echo $this->_value;
}
}
And the regular usage is:
$x = new myClass("SimpleString");
$x->echoThat();
But I have turned the class into a facade type of class in Laravel so it is used:
myClass::echoThat();
How should I utilize the __construct() method in Laravel Facades like the above example?

You should only create a Laravel facade if you really want to use your class like Laravel facades are used. Laravel facades provide a static interface to a class.
This means either rewrite your class so that you can pass your string in anyother way like:
MyClassFacade::echoThat('SimpleString');
Of course you can also modify the underlying class to use for example another method to pass the string:
class MyClass
{
private $_value;
public function setValue($val)
{
$this->_value = $val;
return $this;
}
public function echoThat()
{
echo $this->_value;
}
}
And then call it like:
MyClassFacade::setValue('SimpleString')->echoThat();
You can call the non static methods of you class "statically" if you instantiate your class wherever the Laravel facade accessor is resolved, for example in the service provider:
use Illuminate\Support\ServiceProvider;
class MyClassServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind('myclass', function()
{
return new MyClass();
});
}
}
Laravel will create an instance of MyClass whenever a method on your class is called statically using __callStatic.
Or don't use the Laravel facade and instantiate your class manually like you did it:
$x = new myClass("SimpleString");
$x->echoThat();

You have first to understand that a Facade, in Laravel, is not really your class, you must look at it, as the name says, a forefront or a frontage to your class. Facade is, acually, a Service Locator to your class.
To use this Service Locator you have to create a Service Provider, which will provide the service used by the Facade:
<?php namespace App\MyApp;
use Illuminate\Support\ServiceProvider;
class MyClassServiceProvider extends ServiceProvider {
protected $defer = true;
public function register()
{
$this->app['myclass'] = $this->app->share(function()
{
return new MyClass('the values you need it to instantiate with');
});
}
public function provides()
{
return array('myclass');
}
}
Note that your class is instantiated in the method register() and now is available to your application via the IoC container, so you can do things like:
App::make('myclass')->echoThat();
And now you can also create your Facade to use it:
<?php namespace App\MyApp;
use Illuminate\Support\Facades\Facade as IlluminateFacade;
class ExtendedRouteFacade extends IlluminateFacade {
protected static function getFacadeAccessor()
{
return 'myclass';
}
}
The Facade has only one method and its only purpose is to return the name of your class instance in the IoC container.
Now you can open your app/config/app.php and add the ServiceProvider:
'App\MyApp\MyClassServiceProvider',
also the actual Facade alias:
'MyClass' => 'App\MyApp\MyClassFacade',
and you should be good to use your Facade:
echo MyClass::echoThat();
But note that the way it is made your class __constructor will be always called with the same parameters, in your ServiceProvider, that's how a Facade works, but you have some options:
Use a setter to set a new value for the data in your class instance.
public function setValue($value)
{
$this->_value = $value;
}
Use the Laravel automatic class resolution to provide parameters for your class dinamically:
function __construct (MyFooClass $foo)
{
$this->_foo = $foo;
}
And alter your ServiceProvider to provide no parameters to your class:
$this->app['myclass'] = $this->app->share(function()
{
return new MyClass();
});
Laravel will instantiante MyFooClass automatically for you, if it can locate it in the available source code of your application or if it's bound to the IoC container.
Note that all this assumes that you're not just passing a string in your constructor, the automatic resolution of the IoC container assumes your class has other class dependencies and inject those dependencies automatically (Dependency Injection).
If you really need to just pass strings or single values to constructors of classes that just do some calculations, you don't really need to create Facades for them, you can just:
$x = (new myClass("SimpleString"))->echoThat();
using a simple setter:
$x = new myClass();
$x->setValue("SimpleString");
$x->echoThat();
or, as you were already doing, which is acceptable:
$x = new myClass("SimpleString");
$x->echoThat();
And you can also use the IoC container to instantiate that class for you inside your other classes:
<?php
class Post {
private $myClass;
public function __construct(MyClass $myClass)
{
$this->myClass = $myClass;
}
public function doWhateverAction($value)
{
$this->myClass->setValue($value);
// Do some stuff...
return $this->myClass->echoThat();
}
}
Laravel will automatically pass an instance of your class and you can use it the way you need:
$post = new Post;
echo $post->doWhateverAction("SimpleString");

I found some goode information about model classes in the Facede Lavarel FW in this page
http://laravel.com/docs/eloquent
I assume here is the main explanation about your ploblem, the way to create a enter in database...
// Create a new user in the database...
$user = User::create(array('name' => 'John'));
// Retrieve the user by the attributes, or create it if it doesn't exist...
$user = User::firstOrCreate(array('name' => 'John'));
// Retrieve the user by the attributes, or instantiate a new instance...
$user = User::firstOrNew(array('name' => 'John'));
And here the way to create a simple new instance and initialize its variables:
$user = new User;
$user->name = 'John';
$user->save();
Based on that I assume there isnĀ“t a clear way to use a constructor in Lavarel.

Related

PHP Laravel class returning a couple of classes with static properties - C# static class in static class

In C# I can create static class that have inside a couple of static classes that I can use as namespaces for constants, for example:
public static class ConstantTypes{
public static class ErrorTypes{
public static string Log = "Log";
}
}
Than in app I can use:
ConstantTypes.ErrorTypes.Log
How to do the same in PHP in Laravel?
Now I created two classes:
class LogTypeConstants
{
const MYCONST = 'val';
}
use App\Common\LogTypeConstants;
class AppConstants
{
static function LogTypes() {
$logTypeConstants = new LogTypeConstants();
return $logTypeConstants;
}
}
And in Laravel controller I can use:
$logType = AppConstants::LogTypes()::MYCONST;
Is better way to do the same?
Bold statement, never create static functions unless absolute necessary, a nightmare with dependency injection. With that out of the way, what i feel is the consensus on this, is just to use the constants and access them on the class.
class LogTypeConstants
{
public const MYCONST = 'val';
}
LogTypeConstants::MYCONST; // would access it
An perfect example is how Symfony implements their HTTP status codes on the Response class. In Laravel you would access it like so.
use Illuminate\Http\Response;
public function store() {
...
return $this->response($object, Response::HTTP_CREATED);
}

What's the difference between Laravel automatic injection and manually specifying the dependencies in the constructor body?

I'm using a Repository pattern in my Laravel project. This pattern is not really explained in the official documentation, except for this snippet:
You may type-hint a repository defined by your application in a controller's constructor. The repository will automatically be resolved and injected into the class.
This is my code, in accordance with the documentation:
class CategoriesController extends Controller
{
protected $repo;
public function __construct(CategoriesRepository $repo)
{
$this->repo = $repo;
}
I've type-hinted the CategoriesRepository so it gets automatically loaded by the Service Container.
However, if I directly create a new instance of the CategoriesController class (without using the Service Container), I have to specify that I need a new instance of the CategoriesRepository too, like this:
$example = new CategoriesController(new CategoriesRepository());
Now, let's suppose I write the following code.
class CategoriesController extends Controller
{
protected $repo;
public function __construct()
{
$this->repo = new CategoriesRepository();
}
This way, I don't have to load the class through the Service Container, nor call it by passing a new instance of CategoriesRepository as the argument, because it's automatically created inside of the constructor.
So, my question is: would this be bad practice? What's the difference between type-hinting as a parameter and creating a new instance inside of the constructor?
Here's the beauty of dependency injection:
Complex initialization
class MyController {
public function __construct(A $a) { }
}
class A {
public function __construct(B $b) { }
}
class B {
public function __construct(C $c) { }
}
class C {
public function __construct(D $d) { }
}
class D {
public function __construct() { }
}
Now you can ask laravel to create that class for you e.g:
$controller = make(MyController::class);
or you can do:
$controller = new MyController(new A(new B(new C(new D())))));
In addition you can specify more complex rules on how to create the variables:
app()->bind(D::class, function ($app) {
$d = new D();
$d->setValueOfSomething($app->make(AnotherClass::class));
return $d;
});
Testing
That's one advantage of dependency injection over manual creation of things. Another is unit testing:
public function testSomeFunctionOfC() {
$this->app->bind(D::class, function () {
$dMock = $this->createMock(D::class);
});
$c = make(C::class);
}
Now when you create C the class D will be the mocked class instead which you can ensure works according to your specification.

Testing with Laravel/Lumen facade

So, I'm trying to write a unit test for a piece of code that uses a facade, and it looks like this:
public function test() {
MYFACADE::doSomething();
}
and in the unit test, I am trying to mock the facade call:
MYFACADE::shouldReceive('doSomething')
->once()
->andReturn(false);
The only problem with this is that when Laravel tries to make an instance of underling class for the MYFACADE, it will of course run the constructor, and right there, is hardcoded database connection call. So without changing my facade, and removing database connection from the constructor of the facade class, is there any way to mock facade call without running the constructor of facade ?
UPDATE:
Rest of the setup:
Facade class
class MyFacade extends Facade
{
protected static function getFacadeAccessor()
{
return 'myfacade';
}
}
app.php
$app->register(App\Providers\MyServiceProvider::class);
class_alias('App\Facades\MyFacade', 'MYFACADE');
Service provider:
class MyServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind('myfacade', function()
{
return new myClasThatDoesSomething();
});
}
}
The underlying class used by facade
class myClasThatDoesSomething
{
public function __construct()
{
// opens db connection here
}
public function doSomething()
{
}
}
example class that uses facade:
class TestClass
{
public function testMethod()
{
$returnValue = MYFACADE::doSomething();
return $returnValue;
}
}
Unit test that checks if the testMethod() returns 'testValue';
MYFACADE::shouldReceive('doSomething')
->once()
->andReturn('testValue');
$instance = new TestClass();
$value = $instance->testMethod();
$this->assertEquals('testValue', $value);
First of all Laravel documentation reads:
Facades provide a "static" interface to classes that are available in the application's service container.
Please make sure your MYFACADE follows this pattern. There is no room for constructor, databases, etc. If you test MYFACADE::doSomething() you should mock all other classes that are being used by the function.
Secondly, the following piece of code
MYFACADE::shouldReceive('doSomething')
->once()
->andReturn(false);
mocks the facade itself to test something else that uses MYFACADE::doSomething(). It should return an instance of Mockery which returns false wherever MYFACADE::doSomething() is being called within the test.
EDIT:
Laravel's Facade mock implementation instantiates the underlaying class, which allow to test services with thin constructors. Although it is the best practice, it may not always be possible to do. Assuming you cannot move DB connection logic from the constructor, your best bet will be to mock the service manually:
public function testTestMethod()
{
MYFACADE::swap(\Mockery::mock()
->shouldReceive('doSomething')
->once()
->andReturn('testValue')
->getMock()
);
$instance = new \App\TestClass();
$value = $instance->testMethod();
$this->assertEquals('testValue', $value);
}

Unit Testing Dynamic Factory Class

I have an application which is built on top of another framework. Said framework has a number of value objects, which I need to extend in various ways using the decorator pattern. Lets call the base object Entity and the decorator classes Wrappers. Entities of the same class can have different "types", and a different Wrapper class is required for each of these types to expose functionality specific to that type. This application is not the final layer and does not control what types exist or which classes to use for them (done higher up the chain), so assignment needs to be dynamic.
I have created a factory class that receives an entity and determines the correct wrapper for it. The factory can be assigned a Wrapper class to be used when the entity is of a given type.
<?php
class WrapperFactory
{
protected $default_wrapper = null;
protected $typed_wrappers = [];
public function __construct($default){
$this->setDefaultWrapper($default);
}
public function setDefaultWrapper($class){
if ($this->validateWrapperClass($class)){
$default_wrapper = $class;
}
}
public function getDefaultWrapper(){
return $this->$default_wrapper;
}
public function setWrapperForType($class, $type){
if($this->validateWrapperClass($class)){
$this->$typed_wrappers[$type] = $class;
}
}
public function hasWrapperForType($type){
return array_key_exists($type, $this->typed_wrappers);
}
public function getWrapperForType($type){
if($this->hasWrapperForType($type)){
return $this->typed_wrappers[$type];
}
else{
return $this->getDefaultWrapper();
}
}
public function wrap($entity)
{
$class = $this->getWrapperForType($entity->type);
return new $class($entity);
}
protected function validateWrapperClass($class){
if(class_exists($class) && class_implements($class, WrapperInterface::class)){
return true;
}else{
throw new BadMethodCallException("Wrapper must implement ". WrapperInterface::class . ".");
}
}
}
I'm not entirely sure how to properly Unit Test this class. Is there a way I can mock a class that implements the interface, rather than an object? how can I test that an class assigned to a type is working properly? Do I need to explicitly declare a dummy class or two in my test files or is there a way to mock them?
If you create a Mock object, PHPUnit will create a class that extends the class you created the mock for. Because this works with interfaces as well, you can simply create a mock of your interface and get its class name:
$mock = $this->getMock('WrapperInterface');
$class = get_class($mock);
$this->assertTrue($factory->validateWrapperClass($class));

How to implement a factory class using PHP - Dependancy injection

Take the following code as an example of what i want:
class SomethingController extends Factory
{
private $somethingRepository;
public function __Construct( ISomethingRepository $repo )
{
$this->somethingRepository = $repo;
}
}
class Factory
{
public function __Construct()
{
// The following call to AddBinding would push into SomethingController the new instance of the class denoted in my AddBinding second parameter.
$this->AddBinding( ISomethingRepository, MySQLSomethingRepository);
// So in this case, if the controller i'm extending has a construct parameter of ISomethingRepository, then make the parameter equal a new MySQLSomethingRepository()
// Then if I want to use JSONSomethingRepository in the future, I only have to change the one AddBinding call and the controller will still work.
}
public function AddBinding( $interface, $concrete )
{
// Somehow assign the constructor properties of the extending class as new instances of the bindings i have called upon in the construct of my factory class (see this class's construct)
// Pseudo code:
// ----------------------
$calledClass = get_called_class();
$class = new \ReflectionClass( $calledClass );
$method = $class->getMethod( "__construct" );
$params = $method->getParameters();
foreach( $params as $param )
{
if ( $param == $interface )
{
return new $concrete;
}
}
// /Pseudo code:
// ----------------------
}
}
I want to implement a factory kind of class.
This factory class will be extended by a controller class.
the factory class will look at the construct parameters of the controller class and make new instances of the object based off of my AddBindings method in the factory.
Let's say I wanted to have a MySQLSomethingRepository which has data coming from MySQL... injected into my SomethingController... Somewhere I need to declare that
SomethingController( new MySQLSomethingRepository() )...
which hopefully will be dealt with by my factory class...
The current way i'm doing it is that is forcing a direct coupling with the data source... which is making it very hard to do test cases with:
private $somethingRepository = new MySQLSomethingRepository();
so imagine if i have used this same repository in loads of other controllers and i want to change my database source to some json data and i implement the following repository "JsonSomethingRepository", I have to go and change all of the controllers to:
private $somethingRepository = new JsonSomethingRepository();
How might i implement my Factory class so that it can deal with creating the instances my controller class is demanding inside the AddBindings function?
Design an abstract class in Adapter model and provide some common methods for child class.
You can design both repos with adapters to injected in you controllers.
My recommendtation is to use Abstract class and do it in way below:
class SomethingController extends AbstractController {
}
abstract class AbstractController {
protected $somethingRepository;
public function __Construct(ISomethingRepository $repo) {
$this->somethingRepository = $repo;
$this->AddBinding ( ISomethingRepository, MySQLSomethingRepository );
}
public function AddBinding($interface, $concrete) {
// Somehow assign the constructor properties of the extending class as new instances of the bindings i have called upon in the construct of my factory class (see this class's construct)
}
}
Hope it would be help.

Categories