In Laravel's illuminate/support/Facades/Facade.php file there's a following method:
/**
* Get the registered name of the component.
*
* #return string
*
* #throws \RuntimeException
*/
protected static function getFacadeAccessor()
{
throw new RuntimeException('Facade does not implement getFacadeAccessor method.');
}
Is there any potential of doing that instead of just defining an abstract method, like below?
abstract protected static function getFacadeAccessor();
Why did they possibly want to reinvent the wheel?
I found the following reason here:
This method is designed to be overridden when extending the Facade class to return a string, the key which the service represented by the facade is bound within the container. By default, it throws an exception if not implemented. This gives a more informative message to those creating custom facades than if the framework were to instead use an abstract method.
Related
I've noted that for creating a facade class, laravel provides only name "db"
framework/src/Illuminate/Support/Facades/DB.php
class DB extends Facade
{
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor()
{
return 'db';
}
}
I looked deeper and figured out that this method uses the provided name
framework/src/Illuminate/Support/Facades/Facade.php
protected static function resolveFacadeInstance($name)
{
if (is_object($name)) {
return $name;
}
if (isset(static::$resolvedInstance[$name])) {
return static::$resolvedInstance[$name];
}
return static::$resolvedInstance[$name] = static::$app[$name];
}
I understand first and second If statements.
But I have problems with understanding this:
return static::$resolvedInstance[$name] = static::$app[$name]
As I understood that $app is a protected property of Facade class which contains an instance of \Illuminate\Contracts\Foundation\Application class.
/**
* The application instance being facaded.
*
* #var \Illuminate\Contracts\Foundation\Application
*/
protected static $app;
My two questions:
How is it possible to use an object as an array(static::$app[$name]) if Application class doesn't extends ArrayObject class?
How laravel understands which class to call with providing only a short name 'db'?
Clicking through the Laravel source, I found this. As you can see, ApplicationContract (the private static $app from your question) is implemented by Application. This is in turn derived from Container, which implements the PHP core ArrayAccess interface. Carefully implementing this whole chain eventually makes Applicatin accessible like an array.
Turns out it boils down to good ole' object oriented programming :)
// Illuminate/Foundation/Application.php
class Application extends Container implements ApplicationContract, HttpKernelInterface
^^^^^^^^^ ^-> the private static $app in your question.
// Illuminate/Container/Container.php
class Container implements ArrayAccess, ContainerContract
^^^^^^^^^^^
// PHP core ArrayAccess documentation
/**
* Interface to provide accessing objects as arrays.
* #link http://php.net/manual/en/class.arrayaccess.php
*/
interface ArrayAccess {
You can look this, php manual and use ArrayAccess interface:
http://php.net/manual/en/class.arrayaccess.php
I have model and entity in my CakePHP(latest) app that I want to extend in this way. I want to tell the cake to use ExtraStuff model class instead of app Stuff
App\Model\Table\Stuff
MyPlugin\Model\Table\ExtraStuff
I'm trying to redefine model in the main controller in beforeRender method with methods from Cake\Datasource\ModelAwareTrait
/**
* Override a existing callable to generate repositories of a given type.
*
* #param string $type The name of the repository type the factory function is for.
* #param callable $factory The factory function used to create instances.
* #return void
*/
modelFactory($type, callable $factory)
Or how I can do it? I have a lot of $this->loadModel('Stuff') calls inside the project and I need to tell the app use ExtraStuff class when I need.
Thanks.
Look this
https://book.cakephp.org/3.0/en/controllers.html#loading-additional-models
If you are using a table provider other than the built-in ORM you can link that table system into CakePHP’s controllers by connecting its factory method:
// In a controller method.
$this->modelFactory(
'ElasticIndex',
['ElasticIndexes', 'factory']
);
After registering a table factory, you can use loadModel to load instances:
// In a controller method.
$this->loadModel('Locations', 'ElasticIndex');
I have Entities and Repositories in my project. To simplify, I have
EntityInterface
UserEntity
BusinessEntity
Interface:
interface Entity
{
/**
* #return EntityId
*/
public function getId();
}
Implementations
class UserEntity implements Entity
{
/**
* #return EntityId
*/
public function getId(){
//...do something here for return
return $userId;
}
}
and
class BusinessEntity implements Entity
{
/**
* #return EntityId
*/
public function getId(){
//...do something here for return
return $userId;
}
}
I would like to define a Repository base-functionality, like save, so my interface looks like:
interface Repository
{
/**
* #param Entity $entity
*
* #throws \InvalidArgumentException If argument is not match for the repository.
* #throws UnableToSaveException If repository can't save the Entity.
*
* #return Entity The saved entity
*/
public function save(Entity $entity);
}
Later, I have different interfaces for different type of Repositories, like UserRepository and BusinessRepository
interface BusinessRepository extends Repository
{
/**
* #param BusinessEntity $entity
*
* #throws \InvalidArgumentException If argument is not match for the repository.
* #throws UnableToSaveException If repository can't save the Entity.
*
* #return Entity The saved entity
*/
public function save(BusinessEntity $entity);
}
The above code fails, because Declaration must be compatible with Repository...
however BusinessEntity implements Entity, so it's compatible.
I have many type of entities, so If I can't type-hint, I always need to check, that the passed instance is instanceof what I need. It's stupid.
The following code fails again:
class BusinessRepository implements Repository
{
public function save(BusinessEntity $entity)
{
//this will fail, however BusinessEntity is an Entity
}
}
In general, method parameters have to be contravariant with respect to an inheritance hierarchy or invariant. This means that indeed BusinessEntity would not be "compatible" with Entity when used as a type for a method parameter.
Think of it from a "contract" point of view. Your interface Repository promises that its method save can handle arguments of type Entity. Subtypes inheriting from Repository should be bound to this introduced contract (because otherwise, what sense would it make to define types in the first place, if you cannot be sure what they promises to be able to do?).
Now, if a subtype all of a sudden only accepts more special types, like BusinessEntity, but no longer Entity, the contract's broken. You cannot use BusinessRepository as Repository any more, because you cannot call save with an Entity.
This is counterintuitive at first, but have a look at this: https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)#Contravariant_method_argument_type
Notice the inheritance arrow in the image.
What's to do? Get rid of the idea of inheritance being the holy grail in object oriented programming. Most of the time, it is not, and introduces all kinds of nasty coupling. Favor composition over inheritance, for example. Have a look at Parameter type covariance in specializations.
It fails because you declare methods that takes different arguments in interfaces. There is also question if there is any different logic in saving BusinessEntity than Entity. I think it shouldn't be. So you can omit save function in business entity and save just work on Entity and should know that Entity has "save" method.
The other way is to use factory pattern or abstract factory over inheritance.
Let's say I've
an abstract class with with one abstract method.
2 child classes that define that abstract method in their own way.
A factory that returns an instance of one of 2 child classes based on arguments.
Following is the sample code
abstract class Datalist{
abstract public function render($arg1, $arg2);
}
class Datalist_Table{
public function render($arg1, $arg2){
/* do something here */
}
}
class Datalist_List{
public function render($arg1, $arg2){
/* do something here */
}
}
class DatalistFactory{
/**
* usual stuff
*
* #return Datalist
*/
public static function build($args){
$class_name = 'Datalist_' . $args['type'];
return new $class_name($args['m'][0], $args['m'][1]);
}
}
//in some other file
$list = DatalistFactory::build($args);
$list-> ....
My problem
My IDE (PHPStorm) does not hint on the $list->render(). My guess is that because it has been declared as an abstract.
Question
What should I put in front of #return in PHPDoc for DatalistFactory::build() so that the IDE hints on the functions defined in child classes as well.
P.S.
I've tried instructions in following questions with no success
phpDoc notation to specify return type identical to parameter type
PHPDoc: Is is possible to reference the object property descriptions from factory method docblock?
PHPDoc preconditions
PHPDoc for fluent interface in subclass?
You need to open this file in project. I use phpstorm 8.0.3 create new file in project and insert your code. All works fine =)
I'm developing a service that is being injected a Logger object but I can have 2 different kind of loggers, I'm planning on having a syslog logger and a queue message system logger. Is this possible?
The idea is having an interface:
interface Loggable
{
public function log() ;
}
and 2 classes that implement that interface:
class Syslogger implements Loggable
{
public function log()
{
...
}
}
class QMSLogger implements Loggable
{
public function log($queueName)
{
...
}
}
The only way I could come with is having an array as a parameter and use it on one class and not using on the other one... but that is a little bit smelly :P
You're asking if it's possible: yes it is, but…
If you implement an interface, you must respect its contract.
interface Loggable
{
public function log();
}
This interface's contract is you can call log() without any parameter.
In order to respect that, you can make the parameter optional:
class QMSLogger implements Loggable
{
public function log($queueName = null)
{
...
}
}
This is perfectly valid PHP and it respects the Liskov Substitution Principle. Of course, you must not use that optional parameter when coding against the interface, else you are obviously breaking the interface. Such parameter can be useful only when you are using the implementation (e.g. in some part of the code which is tightly coupled to the QMSLogger).
However this is probably not the solution to your problem as $queueName seems to be a configuration value and it might be better to pass it in the class' constructor (as explained in the other answer).
As stated in the comments, that's not the same interface. If you cannot generalize the interface across all possible logger implementations, make the configuration differences part of the instance constructor:
class QMSLogger implements Loggable {
protected $queueName;
public function __construct($queueName) {
$this->queueName = $queueName;
}
public function log() {
...
}
}
I came across a similar case where I wanted to create an interface that simply ensure any classes that implemented it would have a method of the same name, but would allow for implementation with different parameters.
/**
* Interface Loggable
* #method log
*/
interface Loggable
{
}
Now the Loggable interface can be implemented with different parameters like so.
class Syslogger implements Loggable
{
public function log($key, $value)
{
...
}
}
You can also pass the parameters as an array , in this way you respect the contract from one hand and also be flexible to insert any values with any amount inside the array , check this out :
abstract class FeaturesAbstract
{
/**
* #param array $paramsArray
*
* #return mixed
*/
abstract public function addExecute($paramsArray);
}
And to actually use this method you could send the parameters like this :
$this->abstract->addExecute(array('paramA' => $paramA, 'paramB' => $paramB));
And then inside the concrete implementation you get the parameters like this :
/**
* #param array $paramsArray
*
* #return void
*/
public function addExecute($paramsArray)
{
$a = $paramsArray['paramA'];
$b = $paramsArray['paramB'];
$c = ...
}
Good luck :)