Slim Model db instance - php

sorry for my english.
stack: Slim 3 framework + Eloquent ORM.
Eloquent works as expected with Slim.
I want to use sort of a MVC pattern where thin controllers and fat models(all db queries and other heavy logic).
All I found is how to use it from routes like this:
$app->get('/loans', function () use ($app) {
$data = DB::table('loan_instalment')->get(); // works
$data = $this->db->table('loan_instalment')->get(); // works
...
}
What I want is ability to call public methods from choosen model, something like this:
use \src\models\Instalment;
$app->get('/loans', function () use ($app) {
$data = $this->model('Instalment')->getSomething(12);
...
}
and Model class is:
namespace src\models;
use Illuminate\Database\Eloquent\Model as Model;
use Illuminate\Database\Capsule\Manager as DB;
class Instalment extends Model
{
protected $table = 'loan_instalment';
public function getSomething($id)
{
return $this->table->find($id);
}
// bunch of other methods
}
My app looks like basic Slim skeleton, Eloquent settings:
$capsule = new \Illuminate\Database\Capsule\Manager;
$capsule->addConnection($container['settings']['db']);
$capsule->setAsGlobal();
$capsule->bootEloquent();
$container['db'] = function ($container) use ($capsule){
return $capsule;
};
Is it possible ?

If you want to use MVC pattern, you need to make base controller.
<?php
namespace App\Controller;
use Slim\Container;
class BaseController
{
protected $container;
public function __construct(Container $container)
{
$this->container = $container;
}
public function getContainer()
{
return $this->container;
}
public function __get($name)
{
return $this->container->{$name};
}
public function __set($name, $value)
{
$this->container->{$name} = $value;
}
}
And the container:
// Base Controller
$container[App\Controller\BaseController::class] = function ($c) {
return new App\Controller\BaseController($c);
};
$capsule = new \Illuminate\Database\Capsule\Manager;
$capsule->addConnection($container['settings']['db']);
$capsule->setAsGlobal();
$capsule->bootEloquent();
$container['db'] = function ($container) use ($capsule){
return $capsule;
};
Highly recommended to use static function on models
<?php
namespace App\models;
use Illuminate\Database\Eloquent\Model as Model;
use Illuminate\Database\Capsule\Manager as DB;
class Instalment extends Model
{
protected $table = 'loan_instalment';
public static function getSomething($id)
{
return Instalment::find($id);
}
}
And now you code become:
<?php
use App\models\Instalment;
$app->get('/loans', function ($request, $response, $args) {
$data = Instalment::getSomething(12);
...
}
The controller:
<?php
namespace App\Controller;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use App\models\Instalment;
class HomeController extends BaseController
{
public function __invoke(Request $request, Response $response, Array $args)
{
$data = Instalment::getSomething(12);
// load the template
return $response;
}
}
And the route for the controller
<?php
$app->get('/', App\Controller\HomeController::class);
It looks cleaner, isn't it?
More tutorial:
My Blog
Rob Allen's Blog

You could use the abbility of Slim to use controllers.
Make a basic controller:
// BasicController.php
<?php
namespace src\Controllers;
class BasicController
{
public function model(string $model)
{
return new $model();
}
}
and then in your controllers extend this class and add it to the slim container
//SomeController.php
<?php
namespace src\Controllers;
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;
use \src\models\Instalment as Instalment;
class SomeController extends BasicController
{
public function index(Request $request, Response $response, $args)
{
$this->model(Instalment::class)->getSomethingOutOfDB;
//do something else
}
}
...
//container.php
use \Slim\Container as Container;
$container[\src\Controllers\HomeController::class] = function(Container $container) {
return new \src\Controllers\Homecontroller();
}
...
...
//routes.php
$app->get('/someroute', \src\Controllers\HomeController::class . ':index');
...
Another possibility is to extend your \Slim\App by:
//newApp.php
namespace scr\App
class newApp extends \Slim\App
{
public function model(string $model)
{
return new $model();
}
}
I actually would advice against these both methods, and not load your models in this way, since this is considered bad practice.
It is better just to use:
//routes.php
...
use src\Models\Instalment;
...
...
$app->get('/someroute', function() {
$instalment = new Instalment();
// do something with it...
});

Related

How to write a php unit test case for symfony controller

I am trying to write a unit test case for symfony controller.
Controller Code
namespace AppBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
class Controller1 extends SecuredController
{
public function preExecute(Request $request) {
parent::preExecute($request);
}
public function indexAction()
{
return $this->render('help/index.html.twig');
}
I have tried writing a test case as shown below but it throws an internal error
namespace Tests\AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class Controller1Test extends WebTestCase
{
public function testIndex()
{
$client = static::createClient();
$response = $client->getResponse();
$this->assertEquals(200, $response->getStatusCode());
}
}
Any suggestion would be helpful for me
First of all, is a UNIT test. You here dont need a client. You just need the class you want to test.
namespace Tests\AppBundle\Controller;
use PHPUnit\Framework\TestCase;
use AppBundle\Controller\Controller1;
class Controller1Test extends TestCase
{
public function testIndex()
{
$this->request = $this->getMockBuilder(
'Symfony\Component\HttpFoundation\Request'
)->disableOriginalConstructor()
->getMock();
$controller = new Controller1();
$controller->preExecute($this->request);
$response = $controller->indexAction();
$this->assertEquals(
200,
$response->getStatusCode()
);
}
}
and for example your code could be
namespace Tests\AppBundle\Controller;
use Symfony\Component\HttpFoundation\Response;
class Controller1 extends SecuredController
{
public function indexAction()
{
return new Response();
}
}

How to structure route controllers in slim framework 3 (mvc pattern)

I am working on a Slim Framework based API. Following the mvc pattern, I want my routes to be controller driven with logger and renderer injected to every controller.
As a start point I checked out a number of example mvc slim skeletons, and decided to base my structor on one particular tutorial and sample project ( http://jgrundner.com/slim-oo-004-controller-classes/ )
In this setup, injection is done by adding the router controllers to the app container like this:
$container = $app->getContainer();
$container['\App\Controllers\DefaultController'] = function($c){
return new \App\Controllers\DefaultController(
$c->get('logger'),
$c->get('renderer')
);
};
This then allow for a nice clean route and controller:
route e.g.:
$app->get('/[{name}]', '\App\Controllers\DefaultController:index');
controller e.g.:
namespace App\Controllers;
use Psr\Log\LoggerInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
class DefaultController{
private $logger;
private $renderer;
public function __construct(LoggerInterface $logger, $renderer){
$this->logger = $logger;
$this->renderer = $renderer;
}
public function index(RequestInterface $request, ResponseInterface $response, $args){
// Log message
$this->logger->info("Slim-Skeleton '/' route");
// Render index view
return $this->renderer->render($response, 'index.phtml', $args);
}
public function throwException(RequestInterface $request, ResponseInterface $response, array $args){
$this->logger->info("Slim-Skeleton '/throw' route");
throw new \Exception('testing errors 1.2.3..');
}
}
Extending the default controller keeps the controllers neat, but requires adding every new controller object to the app container first, which seems inefficient and messy when you have a lot of classes.
Is there a better way?
You can make basecontroller that contain the acontainer.
<?php
namespace App\Controller;
use Slim\Container;
class Controller
{
var $container;
public function __construct(Container $container)
{
$this->container = $container;
}
public function __get($var)
{
return $this->container->{$var};
}
}
and the container:
<?php
$container['App\Controller\Controller'] = function ($c) {
return new App\Controller\Controller($c);
};
And the controller
<?php
namespace App\Controllers;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use App\Controller\Controller;
class DefaultController extends Controller{
}

Laravel 5 Resolving dependencies in ServiceProvider

I have a class which acts like a storage (add/get item). I try to bind it as a singleton in one service provider, and resolve it in another's boot method.
The code is changed for simplicity.
app/Providers/BindingProvider.php
<?php namespace App\Providers;
use Illuminate\Support\Facades\Facade;
use Illuminate\Support\ServiceProvider as ServiceProvider;
class MyBindingFacade extends Facade {
public static function getFacadeAccessor() {
return 'my.binding';
}
}
class MyBinding {
protected $items = [];
public function add($name, $item) {
$this->items[$name] = $item;
}
public function get($name) {
return $this->items[$name];
}
public function getAll() {
return $this->items;
}
}
class BindingProvider extends ServiceProvider {
public function register() {
$this->app->singleton('my.binding', function($app) {
return $app->make('App\Providers\MyBinding');
});
}
public function provides() {
return [
'my.binding',
];
}
}
app/Providers/ResolvingProvider.php
<?php namespace App\Providers;
use Illuminate\Support\ServiceProvider as ServiceProvider;
use App\Providers\MyBinding;
class ResolvingProvider extends ServiceProvider {
public function boot(MyBinding $binding) {
$binding->add('foo', 'bar');
// $manual = $this->app->make('my.binding');
// $manual->add('foo', 'bar');
}
public function register() {}
}
app/Http/Controllers/WelcomeController.php
<?php
namespace App\Http\Controllers;
use App\Providers\MyBindingFacade;
class WelcomeController extends Controller {
public function index()
{
dd(MyBindingFacade::getAll()); // debug items
}
}
When I try to debug MyBinding state in my WelcomeController I'm getting empty item array. However, if I uncomment $manual part from my ResolvingProvider it returns an array containing 'foo' => 'bar'. Does it mean IoC resolution is broken in ServiceProvider::boot() method or am I misusing Laravel functionality?
Laravel version: 5.0.28
UPDATE: Added code sample from WelcomeController.
With this:
$this->app->singleton('my.binding', function($app) {
return $app->make('App\Providers\MyBinding');
});
You're saying: my.binding is a singleton and resolves to an instance of App\Providers\MyBinding.
That doesn't mean that App\Providers\MyBinding is registered as singleton too. What you should do instead is this:
$this->app->singleton('App\Providers\MyBinding');
$this->app->bind('my.binding', function($app) {
return $app->make('App\Providers\MyBinding');
});
Because the Facade binding uses $app->make() you should get the same instance you registered with $this->app->singleton() right above.
In the first example you are not using the Facade, you should be using:
use App\Providers\MyBindingFacade as MyBinding;
Which will in fact call make it using 'my.binding'.

InvalidArgumentException in ControllerResolver in Silex

I'm newbie in working with framework and Silex,
I Trying to work with Silex and write my first project.
I use this silex-bootstrap : https://github.com/fbrandel/silex-boilerplate
and now in my app/app.php :
<?php
require __DIR__.'/bootstrap.php';
$app = new Silex\Application();
$app->register(new Silex\Provider\ServiceControllerServiceProvider());
// Twig Extension
$app->register(new Silex\Provider\TwigServiceProvider(), array(
'twig.path' => __DIR__.'/views',
));
// Config Extension
$app->register(new Igorw\Silex\ConfigServiceProvider(__DIR__."/config/config.yml"));
$app->get('/admin', new App\Controller\Admin\AdminDashboard());
return $app;
and in app/Controller/Admin/AdminDashboard.php :
<?php
namespace App\Controller\Admin;
use Silex\Application;
use Silex\ControllerProviderInterface;
use Silex\ControllerCollection;
class AdminDashboard implements ControllerProviderInterface {
function __construct()
{
return "Dashboard";
}
function index()
{
return "Index Dashboard";
}
public function connect(Application $app)
{
return "OK";
}
}
When I trying to access to site I get this error:
http://localhost/project/public
InvalidArgumentException in ControllerResolver.php line 69:
Controller "App\Controller\Admin\AdminDashboard" for URI "/admin" is not callable.
What should I do ?
You're trying to use a controller provider as the actual controller. These are two different things. The provider simply registers controllers with your silex app. Your provider should look something like this:
namespace App\Controller\Admin;
use Silex\Application;
use Silex\ControllerProviderInterface;
class AdminDashboardProvider implements ControllerProviderInterface
{
public function connect(Application $app)
{
$controllers = $app['controllers_factory']();
$controllers->get('/', function() {
return 'Index Dashboard';
});
return $controllers;
}
}
Then you should mount that controller provider to your app in app/app.php.
$app->mount('/admin', new AdminDashboardProvider());
Obviously, this is not very elegant once you get more than a few controllers or if your controllers get big. That's where ServiceControllerServiceProvider comes in. It allows your controllers to be separate classes. I typically use a pattern like this:
<?php
namespace App\Controller\Admin;
use Silex\Application;
use Silex\ControllerProviderInterface;
class AdminDashboardProvider implements ControllerProviderInterface, ServiceProviderInterface
{
public function register(Application $app)
{
$app['controller.admin.index'] = $app->share(function () {
return new AdminIndexController();
});
}
public function connect(Application $app)
{
$controllers = $app['controllers_factory']();
$controllers->get('/', 'controller.admin.index:get');
return $controllers;
}
public function boot(Application $app)
{
$app->mount('/admin', $this);
}
}
The controller looks like this:
namespace App\Controller\Admin;
class AdminIndexController
{
public function get()
{
return 'Index Dashboard';
}
}
Then you can register it with your app in app/app.php like:
$app->register(new AdminDashboardProvider());

ZF2 - Get Data From Doctrine Orm In Dao Layer

My Controller :
<?php
namespace Admin\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Admin\Controller;
use Admin\Service;
class AdminController extends AbstractActionController
{
public function indexAction()
{
$CrudService = $this->getServiceLocator()->get('CrudService');
return new ViewModel(
array('list'=> $CrudService->getList())
);
}
}
Service Layer :
<?php
namespace Admin\Service;
use Admin\Dao;
class CrudService
{
public function getList()
{
$CrudDao=new Dao\CrudDao();
$list=$CrudDao->getList();
return $list;
}
}
Dao Layer :
<?php
namespace Admin\Dao;
class CrudDao
{
public function getList()
{
return
$this->getServiceLocator()->
get('doctrine.entitymanager.orm_default')->
getRepository('Admin\Entity\ProductEntity')
->findAll();
}
}
every things is good work But My Problem is Dao Layer. that give me This
Error : not Found get Service Locator Class
I want get data From Doctrine in Dao Layer and Call Dao Method in Service Layer And Next Call Service With getServiceLocator in Controller
You have to inject all dependencies and use the service manager to get the classes.
In you Module.php you have to register and inject the dependencies:
class Module
{
// ...
public function getServiceConfig()
{
$factories = [
'Admin\Dao\CrudDao' = function (ServiceManager $serviceManager) {
$entityManager = $serviceManager->get('Doctrine\ORM\EntityManager'),
return new CrudDao($entityManager);
},
'Admin\Service\CrudService' = function (ServiceManager $serviceManager) {
return new CrudService($serviceManager);
}
];
return $factories;
}
}
The Dao will receive the EntityManager:
<?php
namespace Admin\Dao;
class CrudDao
{
private $entityManager;
public function __construct($entityManager)
{
$this->entityManager = $entityManager;
}
public function getList()
{
return
$this->entityManager
->getRepository('Admin\Entity\ProductEntity')
->findAll();
}
}
Your CrudService will receive the Service manager, then you can get the CrudDao:
<?php
namespace Admin\Service;
use Admin\Dao;
class CrudService
{
public function __construct($serviceManager)
{
$this->serviceManager = $serviceManager;
}
public function getList()
{
$CrudDao= $this->serviceManager->get('Admin\Dao\CrudDao');
$list = $CrudDao->getList();
return $list;
}
}
And your controller:
<?php
namespace Admin\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Admin\Controller;
use Admin\Service;
class AdminController extends AbstractActionController
{
public function indexAction()
{
$CrudService = $this->getServiceLocator()->get('Admin\Service\CrudService');
return new ViewModel(
array('list'=> $CrudService->getList())
);
}
}

Categories