Slim Model db instance

2019-07-31 16:10发布

问题:

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 ?

回答1:

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:

  1. My Blog
  2. Rob Allen's Blog


回答2:

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...
});