正确构建Laravel项目

时间:2013-11-21 17:09:09

标签: php laravel laravel-4 eloquent

我有一个视图,在一个表格中显示一行10个数字(这是一个统计信息的仪表板)。

它们是今天,明天,后一天的销售信息,由不同的州分开。

目前我有一些代码:

class Transaction extends Eloquent {
    // other methods that aren't relevant

    public function scopeConfirmed($query)
    {
        return $query->where('status', '=', 'Confirmed');
    }

    public function scopeBooked($query)
    {
        return $query->where('status', '<>', 'Cancelled');
    }

    public function scopeDaysAhead($query, $days)
    {
        $start = \Carbon\Carbon::now()->addDays($days)->toDateString();
        $end = \Carbon\Carbon::now()->addDays($days+1)->toDateString();
        return $query->where('date', '>=', $start)->where('date', '<', $end);
    }

    // few other similar scopes
}

然后在我看来,我有:

(简体)

<td>
    {{Transaction::->daysAhead(0)
        ->booked()
        ->count()}}
</td>
<td>
    {{Transaction::->daysAhead(0)
        ->confirmed()
        ->count()}}
</td>
<td>
    {{Transaction::->daysAhead(1)
        ->confirmed()
        ->count()}}
</td>
<td>
    {{Transaction::->daysAhead(2)
        ->confirmed()
        ->count()}}
</td>

所以,我在视图中处理Eloquent调用。实际上,销售人员和地点都有范围,因此显示了10-20个值。

我可以通过使用非常大量的with语句将其移回Controller中,或者当然将其放入控制器中的Array中,但这似乎没有大的帮助。

处理此问题的最佳方法是什么?

2 个答案:

答案 0 :(得分:3)

尽可能减少逻辑视图,不要使用逻辑使控制器混乱。对于大多数人来说,这意味着将所有逻辑填充到模型中。同样,这不是一种有助于编写可重用且更重要的可测试代码的方法。

Laravel的IoC容器和依赖注入非常强大,应该用于以可测试的方式构建应用程序。

我可以理解你为什么要传递查询对象,这是我以前在模型中做的事情。这似乎有道理但很快就会发现使用强大的查询构建器创建一个严格的查询构建器会产生一定数量的湿代码。

我的建议是让你的模型尽可能保持苗条,用它们来创建关系,设置雄辩的属性,验证规则数组等等。将所有逻辑抽象到基于接口的存储库中。为什么?好吧,接口可以通过IoC绑定到它应该解析的类,这意味着它可以很容易地依赖注入和交换(例如Mockery),同时在你可能想要构建的任何替换中保持结构完整性(Mongo,CouchDB)等实施例如。)

namespace Repositories;

interface TransactionInterface {

     public function findAll();
     public function findById($id);
     public function findByDaysAhead($start = 0, $end = 1)

}

对于存储库

namespace Repositories;

use Transaction;

class TransactionEloquent implements TransactionInterface {

    public function findAll()
    {
        return Transaction::all();
    }

    public function findById($id)
    {
        $transaction = Transaction::find($id);

        if ( ! $transaction )
        {
            throw new Exception("Transaction not found");
        }

        return $transaction;
    }

    public function findByDaysAhead($start = 0 , $end = 1)
    {
        // Create one query to return all the data you need
    }
}

然后,您可以在新的自定义ServiceProvider或routes.php中绑定此存储库。

App::bind('Repositories\TransactionInterface', 'Repositories\TransactionEloquent');

现在,您可以依赖性地向您的控制器注入将解析为您的雄辩实现的接口。如果你编写一个不同的存储库实现,你可以简单地将它重新绑定到接口,这意味着它将被用于注入接口的任何地方(例如Mockery类)

class ApplicationController extends BaseController {

    public function __construct(Repositories\TransactionInterface $interface)
    {
        $this->repo = $interface;
    }

    public function getIndex()
    {
        return View::make('index', array('transactions' => $this->repo->findAll());
    }
}

在您的视图中,您只需要对数据和输出进行简单循环,无需逻辑。

显然,您可以在存储库中放置尽可能多的逻辑,因为您可以看到您的控制器,模型和视图仅履行其预期的责任(OOPS单一责任原则)

对于一个非常复杂的问题,这是一个非常简短的答案。我希望这能指导您在Laravel中编写可测试和可重用的模块化代码。

一些“轻松”阅读

放置文件的位置等。

遵循PSR-0规范,我最终会得到像这样的结构

- app
    - {name of app}
        - Repositories
            * TransactionInterface.php
            * TransactionEloquent.php

这两个文件的名称空间现在为namespace {name of app}\Repositories

在编辑器中,您可以将其添加到自动加载对象:

"psr-0": {
    "{name of app}" : "app/"
}

这会将所有兼容PSR-0的命名空间添加到自动加载器中,当您进行更改时可以使用

composer dump-autoload

重建自动加载器并包含新文件(并非总是需要,但比composer update更好,更快)。

有多少个存储库?

每个模型通常最终会有1个以上的存储库。例如,我在前端有一个主干模型集和单个模型。

使用Backbone.sync(param, collection)总是会使用一个repo来处理作为模型数组的输入。其中Backbone.sync(param, model)将使用处理发送的单个模型的仓库。我也有两个Laravel资源控制器来处理这个问题。

服务提供商

我将这些放在我的应用程序文件夹路径中,我的应用程序在此实例中称为“MFL”

- MFL
    - Repositories
    - MFLServiceProvider.php

我将此添加到config\app.php

中的服务提供商数组中
namespace MFL;

use Illuminate\Support\ServiceProvider;

class MFLServiceProvider extends ServiceProvider {

    public function register()
    {
        // Register bindings here, don't use other service providers here
        // you can't be sure they are loaded as of yet
        $this->app->bind('MFL\Repositories\TransactionInterface', 'MFL\Repositories\TransactionEloquent');
    }

    public function boot()
    {
        // Do anything else here with assurance all service providers are
        // fully loaded and the application is ready
    }
}

使用此方法,您不会使用IoC绑定污染routes.php,并且可以将所有代码逻辑分区为服务。出于后一个原因,这是我的首选方法。

答案 1 :(得分:0)

视图作曲家中有第3个选项。在最基本的级别,这些都包含在routes.php中,但我喜欢通过视图路径将它们分解出来,因此composers / transactions.php将处理视图/事务中的所有视图,例如'transactions.dashboard'。鉴于作为观点,作曲家将是...

View::composer('transactions.dashboard', function ($view)
{
    $view->day0_count = Transaction::->daysAhead(0)
        ->booked()
        ->count();
}

$ day0_count现在在视图中可用。

<td>{{ $day0_count }}</td>