I'm using repository pattern in my Laravel project.
what is the good pattern to call service from other service?
For example service will looks like this:
class GetAllUsersService
{
private $userRepository;
public function __construct(UserRepository $repository)
{
$this->userRepository = $repository;
}
public function execute()
{
return $this->userRepository->getAll();
}
}
Now if I want to execute this service from other part of the application I will do something like this:
class AnyClass
{
public function executeUserService()
{
$repository = new UserEloquentRepository();
$service = new GetAllUsersService($repository);
return $service->execute();
}
}
Is it correct way to do it? Is there other ways? Maybe some UI layer should be in between?
I think you have three ways to do it:
1) use method __construct();
class AnyClass
{
private $get_all_users_service;
public function __construct(GetAllUsersService $get_all_users_service)
{
$this->get_all_users_service = $get_all_users_service;
}
public function index()
{
$fetchAllUsers = $this->get_all_users_service->fetchAll();
}
}
2) use the specified services like a parameter of each function needs to use them:
class AnyClass
{
public function index(GetAllUsersService $get_all_users_service)
{
$fetchAllUsers = $get_all_users_service->fetchAll();
}
}
3) use method app() of Laravel helper like this:
class AnyClass
{
public function index()
{
$get_all_users_service = app(GetAllUsersService::class);
$fetchAllUsers = $get_all_users_service->fetchAll();
}
}
Related
Using Symfony 4.4 with autowiring activated, I want to instantiate a class using the design-pattern FactoryMethod.
The class instantiated is a service with autowired arguments passed into the constructor.
It work well if the constructor is the same for each type of class to instantiate inside the factory method.
But, each service to instantiate has to autowire some specific service in order to work.
I found that we could use the "setter dependency injection". Articles describing it:
https://symfonycasts.com/screencast/symfony-fundamentals/logger-trait
https://symfony.com/doc/4.4/service_container/injection_types.html#setter-injection
I tried to implement the setter dependency injection but the code inside is never executed.
Considering the articles, we should enter the setters with the PHPDoc "#required" immediately after the __construct method has been called (from what I understood).
It doesn't work with my code (see below).
Is my implementation correct?
Is there a better way of doing it?
My code looks like:
// Controller
/**
*#Route("/my_action/{param}")
*/
public function my_action (ThingManagerFactory $thingManagerFactory, $param)
{
$thingManager = $thingManagerFactory->get($param);
$thingManager->doSomething();
}
// ThingManagerFactory
class ThingManagerFactory
{
private $firstManager;
private $secondManager;
private $thirdManager;
public function __construct(FirstManager $firstManager, SecondManager $secondManager, ThirdManager $thirdManager)
{
$this->firstManager = $firstManager;
$this->secondManager = $secondManager;
$this->thirdManager = $thirdManager;
}
public function get($param): ThingManagerInterface
{
if($param == 1) {
return new Thing1Manager(
$this->firstManager,
$this->secondManager,
$this->thirdManager,
);
} elseif($param == 2) {
return new Thing2Manager(
$this->firstManager,
$this->secondManager,
$this->thirdManager,
);
}
throw new \InvalidArgumentException("...");
}
}
// ThingManagerInterface
interface ThingManagerInterface
{
public function __construct(
$this->firstManager,
$this->secondManager,
$this->thirdManager,
);
public function doSomething();
}
// Thing1Manager
class Thing1Manager implements ThingManagerInterface
{
(...)
private $spec1Manager;
public function __construct(
$this->firstManager,
$this->secondManager,
$this->thirdManager,
)
{
(...)
}
/**
* #required
*/
public function setSpecificManager(Spec1Manager $spec1Manager)
{
// this code is never called
$this->spec1Manager = $spec1Manager;
}
public function doSomething()
{
// we pass here before going into setSpecificManager
(...)
}
}
// Thing2Manager class
// is similar to Thing1Manager with multiple other specific managers.
Thank you for your help.
In order to use the design-pattern Factory Method with Symfony, use the Service Locator to provide autowire outside a Controller.
Refactor the code to the following:
// Controller
/**
*#Route("/my_action/{param}")
*/
public function my_action (ThingManagerFactory $thingManagerFactory, $param)
{
$thingManager = $thingManagerFactory->get($param);
$thingManager->doSomething();
}
// ThingManagerFactory
use App\Locator\ThingLocator;
class ThingManagerFactory
{
private $locator;
public function __construct(ThingLocator $locator)
{
$this->locator = $locator;
}
public function get($param): ThingManagerInterface
{
if($param == 1) {
return $this->locator->get(Thing1Manager::class);
} elseif($param == 2) {
return $this->locator->get(Thing2Manager::class);
}
throw new \InvalidArgumentException("...");
}
}
// ServiceLocatorInterface
interface ServiceLocatorInterface
{
public function get(string $id);
}
// ThingLocator
class ThingLocator implements ServiceLocatorInterface, ServiceSubscriberInterface
{
private $locator;
public function __ construct(ContainerInterface $locator)
{
$this->locator = $locator;
}
public function get(string $id)
{
if (!$this->locator->has($id)) {
throw new \Exception("The entry for the given '$id' identifier was not found.");
}
try {
return $this->locator->get($id);
} catch (ContainerExceptionInterface $e) {
throw new \Exception("Failed to fetch the entry for the given '$id' identifier.");
}
}
public static function getSubscribedServices()
{
return [
Thing1Manager::class,
Thing2Manager::class,
];
}
}
// ThingManagerInterface
interface ThingManagerInterface
{
public function doSomething();
}
// Thing1Manager
class Thing1Manager implements ThingManagerInterface
{
// ...
private $spec1Manager;
public function __construct($firstManager, $secondManager, $thirdManager, $spec1Manager)
{
// ...
}
// This setter is no more needed. This manager can be added to the constructor method.
// **
// * #required
// */
//public function setSpecificManager(Spec1Manager $spec1Manager)
//{
// if not commented, this code would be called thanks to the Service Locator (which is a Symfony Service Container)
// $this->spec1Manager = $spec1Manager;
//}
public function doSomething()
{
// ...
}
}
I have this code and i´m trying to use a object
<?php
class Controller {
public $_view;
public function __construct() {
$this->_view = new View();
return $this->_view;
}
}
class View {
public $_params = array ();
public function set_params($index_name,$valores) {
$this->_params[$index_name] = $valores;
}
public function get_param($index_name){
return $this->_params[$index_name];
}
}
?>
i would like to do this:
class Index extends Controller {
public function index() {
$model = Model::get_estancia();
$usuarios = $model->query("SELECT * FROM usuarios");
$this->_view->set_params(); // cant be used.
$this->load_view("index/index");
}
}
i would like to use the set_parms function.
but i can't see the View Function, then i can not use.
Can someone explain and advise me a good and safe way?
Correction from Phil: If a __construct() method isn't found, PHP will revert to legacy constructor syntax and check for a method with the same name as the object. In your case the method index() is being treated as the constructor, and is preventing the parent's constructor from loading the view object into the $_view property.
You can force a class to inherit a parent's constructor by defining __construct() in the child and calling the parent's constructor:
public function __construct() {
parent::_construct();
}
Here is the fixed code:
<?php
class Controller {
public $_view;
public function __construct() {
$this->_view = new View();
return $this->_view;
}
}
.
class View {
public $_params = array ();
public function set_params($index_name,$valores) {
$this->_params[$index_name] = $valores;
}
public function get_param($index_name){
return $this->_params[$index_name];
}
}
.
class Index extends Controller {
public function __construct() {
parent::__construct();
}
public function index() {
$model = Model::get_estancia();
$usuarios = $model->query("SELECT * FROM usuarios");
$this->_view->set_params(); // cant be used.
$this->load_view("index/index");
}
}
Say I want to build an application in which models could have different data sources (like MySQL, some API, XML, etc).
What would be the most common approache(s) in order to implement something like this, and what design patterns would be used?
I think DAO is what you are looking for.
Think like:
interface RdbmsDriver {
public function connect();
public function disconnect();
public function query($sql);
public function fetchAll($sql);
}
class MysqliDriver implements RdbmsDriver {
public function connect() { }
public function disconnect() { }
public function query($sql) { }
public function fetchAll($sql) { }
}
class PgsqlDriver implements RdbmsDriver {
public function connect() { }
public function disconnect() { }
public function query($sql) { }
public function fetchAll($sql) { }
}
abstract class RdbmsDao {
protected $driver;
public function __construct(RdbmsDriver $driver) {
$this->driver = $driver;
}
}
interface SomeEntityDao {
public function insert(SomeEntity entity);
public function update(SomeEntity entity);
public function delete(SomeEntity entity);
public function find($entityKey);
public function findAll();
}
/**
* Data from relational databases.
*/
class SomeEntityRdbmsDao extends RdbmsDao implements SomeEntityDao {
public function insert(SomeEntity entity) { }
public function update(SomeEntity entity) { }
public function delete(SomeEntity entity) { }
public function find($entityKey) { }
public function findAll() { }
}
// Use like: new SomeEntityRdbmsDao(new MysqliDriver(...))
/**
* Data from a webservice
*/
class SomeEntityWebServiceDao implements SomeEntityDao {
public function insert(SomeEntity entity) { }
public function update(SomeEntity entity) { }
public function delete(SomeEntity entity) { }
public function find($entityKey) { }
public function findAll() { }
}
class SomeEntityModel {
private $persistance;
public function __construct(SomeEntityDao $persistance) {
$this->persistance = $persistance;
}
}
I would go with the Data Mapper approach.
The mapper is not restricted to databases, it could handle any datasource (file, db, xml, api etc.)
The most important thing is to have your Domain Model separated from the data source. To make the mapper truly interchangeable can be a hard thing, because the datasources are of a different kind. A XML source is not writeable, so you cannot implement an insert method for example.
I have the following PHP code as chain of resposibility, I am using PHP5.4.9.
abstract class Logger
{
protected $next;
public function next($next)
{
$this->next = $next;
return $this->next;
}
public function run(){
$this->invoke();
if(null!=$this->next){
$this->next->invoke();
}
}
abstract public function invoke();
}
class EmailLogger extends Logger
{
public function invoke()
{
print_r("email\n");
}
}
class DatabaseLogger extends Logger
{
public function invoke()
{
print_r("database\n");
}
}
class FileLogger extends Logger
{
public function invoke()
{
print_r("file \n");
}
}
$logger = new EmailLogger();
$logger->next(new DatabaseLogger())->next(new FileLogger());
$logger->run();
the expect output is:
email
database
file
but the actually output:
email
database
I hope to implement chain of resposibility design pattern by PHP language, one abstract class and three or more classes to do something as a chain. but only the first two object works.
Anyting missing? Or PHP can not use this coding style under PHP5.4.9?
Thanks.
Replace
public function run() {
$this->invoke ();
if (null != $this->next) {
$this->next->invoke();
}
}
With
public function run() {
$this->invoke ();
if (null != $this->next) {
$this->next->run ();
}
}
please try $this->next->invoke() change $this->next->run()
I am creating a rest service. I have done all the required methods, what I want to do is some authorization. I have created the table where I store the api-keys, i load them in each method, and it works quite well.
What I need now, is to do some before action that would be called before each method, so i don't have to check if the user is successfully authorized on each method? In simple CI_Controller or in FuelPHP that can be done using public function before, but I dont know how to achieve the same thing in REST_Controller?
Thank you in advance
Here are two controllers. May give you some idea
class MY_Controller extends CI_Controller
{
protected $before_filter = array();
protected $after_filter = array();
private function run_filter($who, $params=array())
{
$filter = $this->{"{$who}_filter"};
if (is_string($filter)) {
$filter = array($filter);
}
if (method_exists($this, "{$who}_filter")) {
$filter[] = "{$who}_filter";
}
foreach ($filter as $method) {
call_user_func_array(array($this, $method), $params);
}
}
public function _remap($method, $parameters)
{
if (method_exists($this, $method))
{
$this->run_filter('before', $parameters);
$return = call_user_func_array(array($this, $method),$parameters);
$this->run_filter('after', $parameters);
}else{
show_404();
}
}
}
class MY_Controller extends CI_Controller
{
public $before_filter = array('check_login');
public $after_filter = array();
private function dashboard()
{
/* other code here */
}
public function check_login()
{
/* Login checking Code here */
}
}