CakePHP replace app model with model from plugin - 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');

Related

Binding to Laravel IoC instead of instantiating again and again

In my app I have a service called "LogService" to log events and other items. I basically need to use this on every controller to log events by users. Instead of having to instantiate this service in each controller, I had two thoughts for accomplishing this.
Option 1: Bind the service into the IoC and then resolve it that way
Option 2: Make a master class with the service in it and then extend it for other classes so they come with the service already bound
I have questions for each of these methods:
Option 1: Is this even possible? If so, would it just be with "App::make()" that it would be called? That way doesn't seem to play too well with IDE's
Option 2: I have done this kind of thing in the past but PHPStorm does not seem to recognize the service from the parent object because it is instantiated by "App::make()" and not through the regular dependency injection.
What would be the best course of action?
Thanks!
You can have it both ways, I think the neatest way would be:
1) Have an interface that describes your class, let's call it LogServiceInterface
2) Create a Service Provider that instantiates your class, like so:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class LoggerServiceProvider extends ServiceProvider
{
/**
* Register bindings in the container.
*
* #return void
*/
public function register()
{
$this->app->bind(LogServiceInterface::class, function($app)
{
return new LogService();
});
}
}
3) Register this service provider in config/app.ph file:
'providers' => [
// Other Service Providers
App\Providers\LoggerServiceProvider::class,
],
4) Now, in controller you can request the instance of something that implements LoggerServiceInterface straight in the constructor:
(Some controller):
<?php namespace App\Http\Controllers;
use Illuminate\Routing\Controller;
use App\Repositories\OrderRepository;
class OrdersController extends Controller {
/**
* The logger service.
* #var LoggerServiceInterface $loggerService
*/
protected $loggerService;
/**
* Create a controller instance.
*
* #param OrderRepository $orders
* #return void
*/
public function __construct(LoggerServiceInterface $loggerService)
{
$this->loggerService = $loggerService;
}
/**
* Show all of the orders.
*
* #return Response
*/
public function index()
{
// $this->loggerService will be an instance of your LoggerService class that
// is instantiated in your service provider
}
}
This way, you have got an easy way to quickly change the implementation of your service, moreover, Phpstorm can handle this very easily.
You will still be able to use app()->make() to obtain an instance of your service.
This, however, will not be automatically picked up by Phpstorm. But you can help it to understand that, all you need to do is to use #var annotation, see:
/**
* #var LoggerServiceInterface $logger
*/
$logger = app()->make(LoggerServiceInterface::class);
That way, Phpstorm will know what to expect from that $logger object.

inject model in laravel controllers constructor

I want to know if this is a good practice to use my model class in controllers in this way :
public function __construct(Rule $rules)
{
$this->rules = $rules;
}
I do not want to repeat myself in my controllers so I want to know what is the best approach for that
You use Dependency Injection - it is very good practice.
According to documentation:
Dependency injection is a fancy phrase that essentially means this: class dependencies are "injected" into the class via the constructor or, in some cases, "setter" methods.
namespace App\Http\Controllers;
use App\User;
use App\Repositories\UserRepository;
use App\Http\Controllers\Controller;
class UserController extends Controller
{
/**
* The user repository implementation.
*
* #var UserRepository
*/
protected $users;
/**
* Create a new controller instance.
*
* #param UserRepository $users
* #return void
*/
public function __construct(UserRepository $users)
{
$this->users = $users;
}
/**
* Show the profile for the given user.
*
* #param int $id
* #return Response
*/
public function show($id)
{
$user = $this->users->find($id);
return view('user.profile', ['user' => $user]);
}
}
In this example, the UserController needs to retrieve users from a data source. So, we will inject a service that is able to retrieve users. In this context, our UserRepository most likely uses Eloquent to retrieve user information from the database. However, since the repository is injected, we are able to easily swap it out with another implementation. We are also able to easily "mock", or create a dummy implementation of the UserRepository when testing our application.
Read also about Service Container - it is powerful tool:
https://laravel.com/docs/5.6/container
It is a good practice for injecting models in controllers, however, the recommended approach is:
Have a use statement at the top of your controller file
Implement it in the functions that requires access to the model, i would not recommend you do it in your controller
If you have a look at the documentation, you will be able to bind the model directly to your route and eliminate some hassle of Model::find(id) https://laravel.com/docs/5.6/routing#route-model-binding
The constructor approach you presented is recommended in using other classes like repositories, singletons, or whatever functionality you wish to inject, see the docs for more info: https://laravel.com/docs/5.6/container
Hope this helps

zend framework 2: how to properly inject factory to controller to get different mapper classes?

I'm using Apigility, built on ZF2. Once request is dispatched to the controller's action, I need to choose proper adapter to hanle request - based on incomming parameters.
Normally, Controller is instantiated by ControllerFactory, where you can provide all dependencies, let say I need som kind of mapper class to be injected. It's easy, if I know, which one I will use in within the controller. It's problematic if I need to let controller decide which mapper to use.
Let's say user is requesting something like getStatus with param 'adapter1' and another user is accessing same action, but with param 'adapter2'.
So, I need to inject adapter1 mapper OR adapter2 mapper, which has similar interface, but different constructor.
What's the proper way how to handle this situation ?
On possible solution is to supply some kind of factory method, which will provide requested adapter, but - using the SM int the model class should be avoided.
Another way is to use SM directly in within Controller's action, but this in not best approach, because I can't reuse 'switch-case' logic for another actions / controllers.
How to handle this, please ?
You could use controller plugins for this.
Like that you can get the adapter inside your controller when you need it without injecting a ServiceManager and without adding all the logic to the factory. The adapter will only be instantiated when you request it in your controller action method.
First you need to create your controller plugin class (extending Zend\Mvc\Controller\Plugin\AbstractPlugin):
<?php
namespace Application\Controller\Plugin;
use Zend\Mvc\Controller\Plugin\AbstractPlugin;
class AdapterPlugin extends AbstractPlugin{
protected $adapterProviderService;
public function __constuct(AdapterProviderService $adapterProviderService){
$this->adapterProviderService = $adapterProviderService;
}
public function getAdapter($param){
// get the adapter using the param passed from controller
}
}
Then a factory to inject your service in the class:
<?php
namespace Application\Controller\Plugin\Factory;
use Application\Controller\Plugin\AdapterPlugin;
class AdapterPluginFactory implements FactoryInterface
{
/**
* #param ServiceLocatorInterface $serviceController
* #return AdapterPlugin
*/
public function createService(ServiceLocatorInterface $serviceController)
{
$serviceManager = $serviceController->getServiceLocator();
$adapterProvicerService = $serviceManager>get('Application\Service\AdapterProviderService');
return new AdapterPlugin($adapterProviderService);
}
}
Then you need to register your plugin in your module.config.php:
<?php
return array(
//...
'controller_plugins' => array(
'factories' => array(
'AdapterPlugin' => 'Application\Controller\Plugin\Factory\AdapterPluginFactory',
)
),
// ...
);
Now you can use it inside your controller action like this:
protected function controllerAction(){
$plugin = $this->plugin('AdapterPlugin');
// Get the param for getting the correct adapter
$param = $this->getParamForAdapter();
// now you can get the adapter using the plugin
$plugin->getAdapter($param);
}
Read more on controller plugins here in the documentation

PHP - How should the controller communicate data with the model layer in a proper MVC pattern

I have been researching so many times, asking experts on stackoverflow for the best practice, but I still could not find the solution, I might have got told the correct answer, but I didn't get it.
I always wanted to create a 'proper' MVC pattern to work on my projects, I have been researching for example this http://www.phpro.org/tutorials/Model-View-Controller-MVC.html but I got told that this is awful design, and I should not use the registery pattern anymore.
So I looked at teresko's answer, even though it's very old, can be seen here How should a model be structured in MVC? and I got the idea there, to use a global factory, which will create other factories
The idea I got from there, is to have a big factory, called ServiceFactory which will create instances of other factories like ModelFactory which will create instances of classes that are serving the model layer, and a ViewFactory, which will serve the view (Pretty useless at the moment I think).
But got something like this:
namespace library\application;
use library\application\exceptions\ClassNotFoundException;
use library\application\exceptions\PackageNotFoundException;
class ServiceFactory {
private $mapper;
private $package;
private $services = array();
public function __construct(DataMapper $mapper) {
$this->mapper = $mapper;
}
/**
* Setting a default package
* #param $package string Package path
* #throws exceptions\PackageNotFoundException
*/
public function setDefaultPackage($package) {
if (is_dir('library' . $package)) {
$this->package = 'library' . $package;
return;
}
throw new PackageNotFoundException("The package: " . $package . " was not found.");
}
/**
* #param $name string Class name
* #param null $constructor IF the class needs constructor parameters, use it.
* #return Factory
* #throws exceptions\ClassNotFoundException
*/
public function create($name, $constructor = null) {
$path = $this->package . "\\" . $name;
if (file_exists($path)) {
if ($constructor) {
return new $path($constructor);
}
return new $path;
}
throw new ClassNotFoundException("The requested class was not found: " . $path);
}
/**
* #param $name string Class name
* #param $object object Object to register
*/
public function register($name, $object) {
$this->services[$name] = $object;
}
/**
* #param $name string Class name
* #return Factory
*/
public function get($name) {
if (isset($this->services[$name])) {
return $this->services[$name];
}
return null;
}
}
This is awesome actually, but in my opinion, OO-wise, it's totally wrong, because it's not object-oriented friendly, since the returning object of S ServiceFactory::create() is anonymous, well fine, It's no longer anonymous, because the purpose of the class is to create instances of factories, sure, just added the interface of the factories in return.
But now the next problem, when I want to use the factories to create objects, that should not have the same interface, will cause problems to my IDE, since it doesn't know what is the object I am going to return, it can be Foo, can be Car and one day it can be HelloWorld.
So I always will have to declare a variable with the object when using it in the controller:
/**
* #var $items Items
* #var $categories CategoryModel
*/
$items = parent::getModelFactory()->create('Items');
$items->setDb(parent::getDatabase());
$categories = parent::getModelFactory()->create('Categories');
$categories->setDb(parent::getDatabase());
Now in one of Teresko's answer(s), it says the following rule: A controller can not create instances of the Model layer
So I assume he means like $foo = new Foo(); $foo->method($db); in the controller, but why not? how should a controller actually communicate the model layer?
The main purpose of me creating that god factory class, is to prevent doing new Object() in the controller class, but since it's calling the factory, which creates the instance, it's still considered as I am creating an instance of a model layer in a controller (I think).
I also could cache objects, using ServiceFactory::register() method, but that doesn't change the situation.
So is the ServiceFactory and factories idea fine ? What is the correct way communcating between the controller and the model layer?
Not really, but there aren't any rules in how to implement MVC as long as you have M V C clearly defined and C acts as a coordinator for M and V.
A factory of factories is clearly over engineering, if you don't need it don't implement it. What you need to understand in order to be easy for you to apply MVC is that 1) MVC is part of the UI layer and 2) Model refers to the bits of other layers (like, for example, Business, Persistence or Application/Service) which are used by the UI.
The controller always communicates directly to the Model, but depending on the use case, the Model means that bits of layers mentioned above. In a 'normal' app, it usually is like this: the controller uses an application service (the MVC Model) to update the business model. The service should be injected in the controller constructor.
For querying purposes, the controller can use a "querying service" or a query only repository so that it won't contain persistence logic (like writing sql). It still the same approach like above, only this time, the Model represents Persistence bits.
So you see, the Mvc Model is actually a facade for other layers used by UI and you want the controller not to have direct access to the business layer for example, because the controller should really just coordinate what model to change and what view model to be rendered. In contrast, an application service will implement an application use case using business/persistence objects.
Everything is really about respecting Separation of Concerns and Single Responsibility Principle

ZF2 Classmethods Hydrator working with RowGateway

i'm trying to implement the RowGateway class to my entities, I already have a form working with the entity and I'm trying to set the hydrator to work with ClassMethods.
I also noticed that ArraySerializable hydrator calls the populate() method or exchangeArray() and this method set the appropriate primary key when editing a row, unfortunately ClassMethods Hydrator doesn't do that.
What would be the best way to set the correct primary key value when using the Classmethod hydrator, should I set this value before binding the entity to the form? Or, should I extend the Classmethod H. to perform this task on initialize?
I'm not fond of using knowledge of the data layer in my entity. When using exchangeArray() you create mapping in the entity itself. I did some research about Zend's hydrators and came across serval posts including this one. Andrew's example of extending the ClassMethods hydrator seemed a good approach to map column names to getters/setters names.
When extending the ClassMethods hydrator you could also implement Zend\Stdlib\Hydrator\HydratorInterface.
For data manipulation use hydrator strategies.
http://framework.zend.com/manual/2.0/en/modules/zend.stdlib.hydrator.strategy.html
http://juriansluiman.nl/nl/article/125/strategies-for-hydrators-a-practical-use-case
To sepperate your entity over mutliple data sources you can use hydrator filters. For example, by default the ClassMethods hydrator extracts all entity methods starting with get.
http://framework.zend.com/manual/2.1/en/modules/zend.stdlib.hydrator.filter.html
You could extend Zend\Stdlib\Hydrator\ClassMethods and do any transformations you require here, assuming this is what you mean.
You can then use mapField to map from one of your fields to the correct id field name.
namespace Application\Model;
use Zend\Stdlib\Hydrator\ClassMethods;
class MyHydrator extends ClassMethods
{
/**
* Extract values from an object
*
* #param object $object
* #return array
* #throws Exception\InvalidArgumentException
*/
public function extract($object)
{
$data = parent::extract($object);
$data = $this->mapField('id', 'user_id', $data);
return $data;
}
/**
* Map fields
*
* #param type $keyFrom
* #param type $keyTo
* #param array $array
* #return array
*/
protected function mapField($keyFrom, $keyTo, array $array)
{
$array[$keyTo] = $array[$keyFrom];
unset($array[$keyFrom]);
return $array;
}
}
Alternatively you could make a getter and setter for the id field you need setting/getting, for example if you have an id called 'user_id' :
public function getUserId() { .. }
public function setUserId($id) { .. }

Categories