I am building my website in Slim 3 MVC framework.I need to call some commonly used functions for controller (Eg: For Alias of page title I am using a function called function getAlias(){.....}).
Where I have to create those functions? How can I call inside controllers?
There's a number of ways to do this. If the functions have no side effects, then one option would be to have a utilities class with static methods in it it.
Another option would be to extend all your route actions from a common class and use that:
// CommonAction.php
class CommonAction
{
protected function getAlias() { }
}
// HomeAction.php
class HomeAction extends CommonAction
{
public function __construct(/*dependencies here*/) { }
public function __invoke($request, $response, $args) {
// route action code here
return $response;
}
}
// index.php
$app = new Slim\App(require('settings.php'));
$container = $app->getContainer();
$container[HomeAction::class] = function ($c) {
return new HomeAction(/*dependencies*/);
}
$app->get('/', HomeAction::class);
$app->run();
If the functionality is part of your domain layer, then inject those classes into your route actions as a dependency.
Related
I'm trying to add a custom assertion to the TestReponse class so I can make something like this:
$response = $this->json('POST', '/foo/bar');
$response->myCustomAssertion();
I tried creating an App\TestResponse class that extends the original one and then binding it in the App\Provider\AppServiceProvider class.
public function register()
{
$this->app->bind('Illuminate\Foundation\Testing\TestResponse', function ($app) {
return new App\TestResponse();
});
}
But $response->json() is still returning the original one and not my own implementation.
How can I extend the TestResponse class?
If you want a little more fine-grained control, you can also extend the Illuminate\Foundation\Testing\TestResponse, as you have done, and then override the createTestResponse method in your TestCase class to return an instance of your custom response class:
// Laravel 8 and above
protected function createTestResponse($response)
{
return tap(App\TestResponse::fromBaseResponse($response), function ($response) {
$response->withExceptions(
$this->app->bound(LoggedExceptionCollection::class)
? $this->app->make(LoggedExceptionCollection::class)
: new LoggedExceptionCollection
);
});
}
// Before Laravel 8
protected function createTestResponse($response)
{
return App\TestResponse::fromBaseResponse($response);
}
From Illuminate\Foundation\Testing\Concerns\MakesHttpRequests.
The TestResponse class uses the Macroable trait so you can add macro functions at runtime.
TestResponse::macro('nameOfFunction', function (...) {
...
});
You can add this to a Service Provider's boot method or somewhere before you need to make the call to that macro'ed method.
I read the documentation here about creating middleware. But which folder or file i must be create it? Documentation is not contain this information.
Under the src folder i have middleware.php.
For example i want to get post information like this:
$app->post('/search/{keywords}', function ($request, $response, $args) {
$data = $request->getParsedBody();
//Here is some codes connecting db etc...
return json_encode($query_response);
});
i made this under the routes.php but i want to create class or middleware for this. How can i do? Which folder or file i must be use.
Slim3 does not tie you to a particular folder structure, but it does (rather) assume you use composer and use one of the PSR folder structures.
Personally, that's what I use (well, a simplified version):
in my index file /www/index.php:
include_once '../vendor/autoload.php';
$app = new \My\Slim\Application(include '../DI/services.php', '../config/slim-routes.php');
$app->run();
In /src/My/Slim/Application.php:
class Application extends \Slim\App
{
function __construct($container, $routePath)
{
parent::__construct($container);
include $routePath;
$this->add(new ExampleMiddleWareToBeUsedGlobally());
}
}
I define all the dependency injections in DI/services.php and all the route definitions in config/slim-routes.php. Note that since I include the routes inside the Application constructor, they will have $this refer to the application inside the include file.
Then in DI/services.php you can have something like
$container = new \Slim\Container();
$container['HomeController'] = function ($container) {
return new \My\Slim\Controller\HomeController();
};
return $container;
in config/slim-routes.php something like
$this->get('/', 'HomeController:showHome'); //note the use of $this here, it refers to the Application class as stated above
and finally your controller /src/My/Slim/Controller/HomeController.php
class HomeController extends \My\Slim\Controller\AbstractController
{
function showHome(ServerRequestInterface $request, ResponseInterface $response)
{
return $response->getBody()->write('hello world');
}
}
Also, the best way to return json is with return $response->withJson($toReturn)
I am using Slim Framework 3. I want to inject $logger defined in dependencies.php into a Router Controller class. Below is what I do, is there a better way?
routes.php
$app->get('/test', function($request, $response, $args){
$controller = new AccountController($this->get('logger'));
return $controller->test($request, $response, $args);
});
AccountController
class AccountController{
private $logger;
function __construct($logger){
$this->logger = $logger;
}
public function test($request, $response, $args){
$this->logger->info('i am inside controller');
return $response->withHeader('Content-Type', 'application/json')->write('test');
}
}
In Slim Framework 3 documentation, the proper way of using a Route Controller should be:
$app->get('/test', 'AccountController:test');
But how do I inject $logger into AccountController when I choose to code my Route Controller in this more "elegant" way?
In terms of making your controller easier to test, you should inject the logger into the controller via the constructor.
AccountController looks like this:
class AccountController
{
protected $logger;
public function __construct($logger) {
$this->logger = $logger;
}
public function test($request, $response, $args){
$this->logger->info('i am inside controller');
return $response->withJson(['foo' => 'bar']);
}
}
Set up in index.php is something like:
$container = $app->getContainer();
$container[Logger::class] = function ($c) {
$logger = new \Monolog\Logger('logger');
return $logger;
};
$container[AccountController::class] = function ($c) {
$logger = $c->get(Logger::class);
return new AccountController($logger);
};
$app->get('/test', 'AccountController:test');
Note that if you make the format route callable be a string of 'class name' colon 'method name', then Slim 3 will call the method for you after extracting the controller class from the DI container. If the class name is not a registered key with the container, then it will instantiate it and pass the container to the constructor.
According to the container resolution docs, you should be able to access your logger through the container, inside your controller:
AccountController
class AccountController
{
protected $ci;
//Constructor
public function __construct(ContainerInterface $ci)
{
$this->ci = $ci;
}
public function test($request, $response, $args)
{
$this->ci->get('logger')->info('i am inside controller');
return $response->withHeader('Content-Type', 'application/json')->write('test');
}
}
When you call $app->get('/test', 'AccountController:test');, Slim should automatically pass the container into AccountController's constructor.
That being said, this is more of a convenience feature than an example of great design. As Rob Allen explains in his answer, you can achieve better modularity, and thus more easily tested code (if you're using unit tests), by injecting the controllers into the application container, rather than injecting the container into each controller.
Take a look at his example Slim application. If you look at, for example AuthorController, you can see how with this design controller classes no longer depend on the magical container providing all the services. Instead, you explicitly state which services each controller will need in the constructor. This means you can more easily mock the individual dependencies in testing scenarios.
Lets say I am building an OOP-based user authentication system, and I would like to incorporate the following principles: Direct Injection, Inheritance, Encapsulation, Polymorphism and the Single Responsibility Principle.
My background in programming is has always relied on procedural programming, and thus, am finding it difficult to really put these practices into correct use.
Assume I have these classes:
class Config
{
public function set($key, $value);
public function get($key, $default = null);
}
class User
{
public function __construct(PDO $dbh, $id = null);
public function setProfile(Profile $profile);
}
class Auth
{
public function __construct(Config $config);
public function login($username, $password, $keepLoggedIn = true);
public function isLoggedIn();
public function getLoggedInUser();
public function logout();
public function register(array $data);
}
class Session
{
public function start($sessionName = null);
public function write($key, $value);
public function read($key, $default = null);
}
class Profile
{
public function setAddress(Address $address);
public function setName($name);
public function setDOB(DateTime $date);
public function getAge();
}
class Validator
{
public function validate($input);
}
I have intentionally left off the function bodies to keep things simple.
To the best of my knowledge, I believe I'm using the principles correctly. However, I am still unclear as to how you would connect classes like: the Validator to the User model, the User model to the Auth and the Session to the Auth class. All of which depend on each other.
You are on the right track. The way these classes connect to each other is called extending. I tend to go towards an MVC setup, meaning Model, View, Controller.
Your logic goes into the controller, all your DB queries and concrete back end methods go in the model. The controller receives requests and returns responses. It's the middleman. It talks to the back end after a request has been made to it, and feeds the front in via response.
So you have a core controller (keep it bare minimal), then each class you make extends the core controller. So your controller is where you tie all this together.
<?php
//your main core controller, where you load all these things you need avilable, so long as this class is extended
class CoreController {
public $auth
public $session;
public $view;
function construct__ ()
{
$this->auth = instantiateAuthClassHere();
$this->session = instantiateSessionClassHere();
$this->view = instantiateViewClassHere();
}
public function anotherHelperForSomething(){
//helper stuff for this method
}
}
//index, page, or content controller, depending on how many you need, i.e. if you want a controller for each page, thats fine, e.g indexController, etc..
//this is the middle man, has logic, receives requst, returns response to view.
class Controller extends CoreController {
public function index (){
$userModel = new userModel();
//do something with this
$session = $this->session;
$content = 'some html';
$userInfo = $userModel->getUsers();
$view = $this->view->render( array(
'content' => $content,
'userInfo' => $userInfo,
));
return $view;
}
}
//Core LIbraries
class Validator {
//your validator stuff
}
//Core LIbraries
class Session {
//your validator stuff
}
//Core LIbraries
class Auth {
//your validator stuff
}
class CoreModel{
public $validator;
function __construct(){
$this->validator = instantiateValidatorClassHere();
}
}
//a user model class (back end). you want a model class for each db table pretty much.
class UserModel extends CoreModel {
// if you need the validator anywhere inside this class, its globally available here inside any class that extends the CoreModel, e.g. $this->validator->methodName()
public function getUsers (){
$sql = 'SELECT * from users';
$result = $db->get($sql);
return $result;
}
}
Notice, on the Controller, this is a generic name for something like indexController, or anything custom. Also, I have the word extends there. It inherits all the objects from the parent that it extends. Inside it, now they will be available via $this->. See my example where I get $this->session.
Try to avoid constructs - you probably don't need them anywhere except for the core, and under special circumstances, which you might then need to check for yourself before you do even that. I dont use constructs much anymore. It can be a bit clunky and unmanageable.
I use Cakephp 2.1 and I need to call a component method which resides in a plugin, from a view helper:
The component is here:
/app/Plugin/Abc/Controller/Component/AbcComponent.php
The helper is here:
/app/View/Helper/SimpleHelper.php
I tried inside helper:
App::import('Component', 'Abc.Abc');
$this->Abc = new Abc(); or $this->Abc = new AbcComponent;
or
$this->Abc = $this->Components->load('Abc.Abc');
inside the controllers this component works with no problem.
I know this isn't recommended (MVC design etc.) but if I don't use it this way I need to duplicate a lot of code. I need to make something like:
MyHelper extends Helper{
$simpleVar = Component->get_data();
}
I use CakePHP 2.4
This is how I successfully call Component from a Helper:
App::uses('AclComponent', 'Controller/Component');
class MyHelper extends AppHelper {
public function myFunction() {
$collection = new ComponentCollection();
$acl = new AclComponent($collection);
// From here you can use AclComponent in $acl
if ($acl->check($aro, $aco) {
// ...
}
}
}
Passing data from CakePHP component to a helper
This seems to be a very nice way to handle this.
I tried working the way you are before, and, although it seems to be a nice immediate solution, in the long run, it is better to just work with the component and helper as 2 separate entities in your controller.
lee
You can put logic in trait and use this from component and helper, if your porpouse is to use the same business logic in different places, to avoid duplication code.
By example
the trait (file app/Lib/NameOfTrait.php or app/PluginName/Lib/NameOfTrait.php)
trait NameOfTrait {
public function theTraitFunc($a, $b) {
// Code here
}
}
The Component:
App::uses('Component', 'Controller');
App::uses('NameOfTrait', 'PluginName.Lib');
class NameOfComponent extends Component {
use NameOfTrait;
private $member;
private $controller;
public function __construct(ComponentCollection $collection, $settings = array()) {
parent::__construct($collection, $settings);
$this->member = $settings['memberName'];
}
function startup(Controller $controller) {
$this->controller = $controller;
}
/**
* Wrap function call of trait function,
* I think the function doesn't have the same name,
* I don't try this but I think is obvious,
* to avoid the function to call itself
*/
public function theTraitFuncWrap($a) {
return $this->theTraitFunc($a, $this->member);
}
}
Do The same for the Helper.
I hope this help someone, bye :)