Laravel深刻学习5 - 应用架构

声明:本文并不是博主原创,而是来自对《Laravel 4 From Apprentice to Artisan》阅读的翻译和理解,固然也不是原汁原味的翻译,能保证90%的原汁性,另外由于是理解翻译,确定会有错误的地方,欢迎指正。php

欢迎转载,转载请注明出处,谢谢!程序员

应用架构

简介

这一章是哪出戏?对于使用框架建立应用这是很是广泛的。不少开发者会提出这样的问题,由于在他们脑仁里已经存在这样的观念,“模型”就是“数据库”。因此一般,控制器被用来和HTTP交互,模型就是和数据库_打交道_,视图就是还有HTML代码的那部分。可是,对于那些好比发送邮件的类、验证数据类、访问接口的类该怎么区分呢?本章咱们就使用Laravel构建好的架构进行探讨,打破那些固话在你心中的概念,让开发回归本质。数据库

MVC会弄死你的

阻碍咱们的一种设计即:M-V-C。模型,视图,控制器,这种框架思惟已经控制开发人员不少年了。这种思惟来源于Ruby On Rails。若是,让一个程序员去解释什么是“模型”,一般都会听到将其和“数据库”关联的答案。听说,模型_就是_数据库。模型包含了数据库的一切。可是,很快你就会发现,在简单的数据库访问类之上还有不少额外的逻辑。他须要咱们进行数据验证,调取额外的服务,发送邮件,等等。json

什么是模型?数组

模型如今已经变的模棱两可,很难具体指代什么。根据开发中遇到的那么多词汇,咱们能够理解认为,他就是为了将应用切分红小而清晰,具备特定职责的类。网络

那么,这种困境中的解决方案是什么?不少开发人员会在控制器之上添加更多的逻辑。当控制器变的很大的时候,须要复用其余控制器中的一些逻辑层。不少人会错误的认为_须要_在当前控制器调用其余控制器,而不是讲逻辑抽象成单独的类。这种模式一般称为“HMVC”。不幸的是,这也是糟糕的设计,一般控制器会很复杂。架构

HMVC(一般)预示着糟糕的设计app

当以为需要在控制器中调用其余控制器?这意味着当前设计是糟糕的,控制器里面的业务逻辑太负责。咱们能够讲逻辑抽象成通用类,以便在其余控制器中进行调用。composer

总会有更好的程序设计。咱们须要忘记之前在脑海中残留的那种“模型”的设计理念,干脆让咱们删除模型目录,并从新开始。框架

再见,模型

是否已经把你的models目录删除?若是没有,不要紧,别再去理他。咱们来在app下创建一个新文件夹,简单的起个应用名字便可QuickBill,在后续的讨论中,咱们以前举例的那些例子都会出现。

注意使用场景

记住,若是你建立的是小型的Laravel应用,在models下建立几个Eloquent模型仍是很合适的。而在本章中,咱们关注的是拥有更多“层次”架构的复杂应用。

咱们已经有了app/QuickBill目录,他和controllersviews目录同级。咱们能够在QuickBill下建立一些其余目录,好比RepositoriesBilling目录。建好目录以后,记得在composer.json注册PSR-0自动加载。

"autoload": {
    "psr-0":    {
        "QuickBill":    "app/"
    }
}

如今,咱们把Eloquent类放到QuickBill根目录下,咱们就能轻松的访问到QuickBillUser,以及QuickBillPayment等。在Respositories目录中建立PaymentRepositoryUserRepository类,并编码数据访问的方法getRecentPayments以及getRichestUser。在Billing目录则包含使用第三方服务,诸如“Stripe”、“Balanced”的类和接口。上述目录结构以下:

// app
    // QuickBill
        // Repositories
            -> UserRepository.php
            -> PaymentRepository.php
        // Billing
            -> BillerInterface.php
            -> StripeBiller.php
        // Notifications
            -> BillingNotifierInterface.php
            -> SmsBillingNotifier.php
        User.php
        Payment.php

数据验证放哪

这个问题一般让咱们头大。能够考虑把他们放到“实体”类中,好比User.php或者Payment.php中,方法名能够叫:validForCreationhasValidDomain。或者也能够建立一个命名空间为ValidationUserValidator类,并注入到repository类中。两种方法看我的喜爱。

摆脱models目录,咱们就能冲破枷锁,实现好的设计。固然,咱们建立的不少项目都会有类似之处,不管多么复杂的项目也都会有数据接入(存储)层,以及其余服务层等等。

不要惧怕文件夹

不要由于多建文件夹而惧怕,他是组织应用程序的很好方式。一般咱们但愿以此讲应用分割成很小的组件,每一个组件都有本身特定的职责。别被“模型”束缚了思惟。就像上面举的例子,咱们能够建立一个Repository来存放全部数据接入的相关类。

到处皆分层

你应该已经注意到,好的应用设计应拥有明确的职责划分,有明确的逻辑分红。控制只是用来接收HTTP请求并请求逻辑处理类。业务处理层_才是_整个应用的中心。它包含了像数据获取,数据验证,支付处理,邮件发送类库,以及应用中的各类函数等。事实上,业务逻辑无需感知“网络”,网络仅仅接入应用的传输机制,他不该超出应用中的路由和控制器的范畴。好的架构是经得起考研的,是由清晰代码组成的可持续发展的架构。

例如,咱们使用向控制器中传入网络请求输入来替代在类中直接访问网络请求实例的方法。简单的改动即将类从“网络”中解耦出来,这种方式也不用担忧在测试时对请求的再次模拟了:

class BillingController extends BaseController{
    public function __construct(BillerInterface $biller)
    {
        $this->biller = $biller;
    }
    public function postCharge()
    {
        $this->biller->chargeAccount(Auth::user(), Input::get('amount'));
        return View::make('charge.success');
    }
}

chargeAccount方法能够很容易进行测试,只需传入测试数据用到的整型数据,而不是使用一个含有RequestInput类的BillerInterface接口的实现类库来做为参数传入该方法。

职责分离是编写健壮应用的关键。这种关键就是一个类是否管的太多。你应该时常问本身:“是否是这个类还要关心X?”,若是答案是“不”,就将逻辑抽象出来,并用依赖注入的方式处理。

改变的缘由很简单

决定类库是否足够职责分离的一个很是有用的方法就是检验本身为何要更改这些代码。好比,在调整通知逻辑的时候,是否Biller接口的实现也要修改?固然不,Biller的实现只和支付有关,只需按照约定和通知逻辑交互。保持这样的思惟观念,就能帮你快速改进应用中的各个部分,使之变得健壮起来。

“瓶瓶罐罐”都放哪

当使用Laravel开发应用的时候,会常常有这样的疑问,不少“东西”不知道放哪。好比,“helper”函数放哪?事件坚挺程序放哪?视图组件又该在哪?答案可能会让你凌乱:“哪均可以”!Laravel没有文件该归属哪里的概念。然而这个答案并非让人满意的,在继续深刻以前,让咱们先就上面的问题探讨下。

辅助函数

Laravel的辅助函数放在support/helpers.php文件中。或者你也想建立这样一个本身的辅助函数文件,“start”目录就是个不错的地方。在请求应用时,start/global.php都会被引用到,咱们能够在这添加上加载本身的helpers.php文件:

// Within app/start/global.php

require_once __DIR__.'/../helpers.php';

事件监听器

事件监听器固然不能属于routes.php文件,放在start文件也不合适,咱们要另择地方安放他。服务提供器的目录就不错,以前咱们知道,服务提供不只是容器注册绑定的服务,还能够作不少其余事情。它能够讲不少监听器组织起来,这种方式是代码清理整洁,也不影响应用逻辑。视图组件也能够放到这个位置,他和监听器实际上是相似的,都能收纳在服务提供器中。

好比,用服务提供器组织监听器:

<?php namespace QuickBillProviders;

use IlluminateSupportServiceProvider;

class BillingEventsProvider extends ServiceProvider{

    public function boot()
    {
        Event::listen('billing.failed', function($bill)
        {
            // Handle failed billing event...
        });
    }
}

建立完提供器后,只须要简单的在app/config/app.php配置中providers数组添加上它就好。

注意boot方法

记住,上例中咱们使用boot的缘由,register方法仅仅是用来将服务注册到容器的方法。

错误处理

若是应用中咱们自定义了错误处理,别接管在“start”文件中,一样,像事件监听器同样,最好还放在服务提供器中进行组织。提供器能够像这样命名QuickBillErrorProvider,并在boot方法中讲全部自定义的错误处理注册进来,重申一下:咱们要将这些代码和咱们的逻辑分离开来。最终,自定义的错误处理程序以下:

<?php namespace QuickBillProviders;

use App, IlluminateSupportServiceProvider;

class QuickBillErrorProvider extends ServiceProvider {

    public function register()
    {    
        //
    }

    public function boot()
    {
        App::error(function(BillingFailedException $e)
        {
            // Handle failed billing exceptions ...
        });
    }
}

简洁的方案

固然在只有一两个错误处理方式的状况下,把他放到“start”文件也是一种简洁的方式。

其余

一般,类库应该以PSR-0规范组织在咱们的应用中。命令式代码如事件监听、错误处理、以及其余“注册”类型的服务最好组织在服务提供器中。基于如上原则,咱们就能决策出代码的组织规律。有一点不要太犹豫,Laravel是为了让工做方便于咱们的业务,这也是Laravel的宗旨。寻找适合本身应用的结构,并分享给其余人。

如上,咱们能够为全部自定义的服务提供器添加一个命名空间Providers并建立目录组织起来:

// app
    // QuickBill
        // Billing
        // Extensions
            //Pagination
                -> Environment.php
        // Providers
            -> EventPusherServiceProvider.php
        // Repositories
        User.php
        Payment.php

上例中,有两个命名空间ExtensionsProviders,自定义的服务放到Providers目录下,对框架扩展的组件以Extensions命名空间的方式组织到同名目录下。

相关文章
相关标签/搜索