文档章节

laravel (5.1) & Ember.js (1.13.0) 的整合

Lee的白板报
 Lee的白板报
发布于 2015/08/15 16:21
字数 2092
阅读 244
收藏 5

Lavavel 不必过多介绍了, 作为全世界最流行的PHP框架,有着清晰的架构、完善的文档、丰富的工具等等,能够帮助开发者快速构建多页面web应用程序。

然而,随着技术的发展,web程序的另一面——客户端,正在变得越来越多元(PC,手机,平板,其他专用设备等)。所以需要一种统一的机制,方便服务器与不同的设备进行通信。Restful API 就是基于这个思想被提出来的。

阮一峰给出了对Restful架构的总结:

  1. 每一个URI代表一种资源;

  2. 客户端和服务器之间,传递这种资源的某种表现层;

  3. 客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。

从行为上讲,就是服务器在约定好一套资源交互规则之后,依据该规则,通过统一的API接口与不同的前端设备进行交互。服务器只需要专注于数据的存储与分析,或曰业务逻辑的实现。在不同客户端上,其表现逻辑和交互逻辑与服务器端的业务逻辑实现了双重分离——逻辑分离与物理分离。

如果前后端只有资源(数据)的交互,那么页面路由自然当交给前端控制,相当于前端在首次加载页面后就不再进行全页面的刷新,所有的数据通过ajax从后端随取随用,所有的表单提交也是同样方法,这就是一个单页面应用(Single Page Application, SPA)。

Ember.js是一个模块化的前端框架,基于MVC理念,它提供了UI绑定、模板系统、路由系统等功能,非常适合SPA的快速开发。特别是Ember还提供了一个完整的命令行开发包——Ember Cli,不仅省去了繁琐的开发环境配置,还提供了丰富的开发与构建工具,例如,你立即就可以用CoffeeScript甚至ES6进行开发,相应的解释器已经随开发包安装妥当,在启动 ember server 的情况下,你也不必每次改动都在命令行键入 ember build,系统自动识别文本改动并进行解释与合并,将编译好的文件放在 dist/ 目录下。

然而,当我把 Laravel 和 Ember.js 分别配置妥当之后,发现我并不能马上撸起袖子写代码,因为他们并不是为彼此而生的。例如,此时我就面临着两个问题:

  1. Laravel 与 Ember.js 有各自的路由系统,如何让 laravel 出让自己对 URL 的控制?

  2. 用户授权通常是由服务器管理和维护的,Laravel 提供完整的 authentication 方案,但在SPA 中后端不得不出让一部分权限控制给前端(主要是页面访问权限和为ajax授权),解决这个问题的最佳实践是怎样的?

我相信之前已经有很多人遇到类似问题,该问题可能有通用的原则指导,但在操作层面与具体的框架相关,限于篇幅,本文讨论第一个问题。第二的问题另作回答。




定义API接口

在laravel中,laravel/app/Http/routes.php 文件是所有 URL 的入口,所有的 URL 和相应的处理函数都应当在这里定义。

// laravel/app/Http/routes.php

Route::group( array( 'prefix' => 'api/v1' ), function()
{

    // USERS API ==================================

    Route::get('users/{id}', 'UserController@getById');
    Route::delete('users/{id}', 'UserController@destroyById');
    Route::put('users/{id}', 'UserController@updateById');
    Route::post('users', 'UserController@storeNew');


    // OTHER API ==================================

    // ......

});

我将所有API放入一个路由组,这个组约定API请求必须以 api /【版本号】作为前缀,例如需要服务器返回 id=5 的用户信息,应当向如下地址发出 GET 请求:

    http://your_demain/api/v1/users/5    

服务器收到该请求后将 request 对象传递给 UserController 的 getById 成员方法做处理。

我还在该文件中定义了用户授权的相关接口,并将它们分在一组:

// laravel/app/Http/routes.php

Route::group( array ( 'prefix' => 'auth' ), function()
{
    Route::post('login', 'Auth\AuthController@postLogin');
    Route::get('logout', 'Auth\AuthController@getLogout');
    Route::post('register', 'Auth\AuthController@postRegister');
});

三个接口分别提供登录、登出和注册功能。

好了,laravel 的路由只需要做这么多事情。


将其他URL的控制权交给前端

Ember页面启动时以 ember/dist/index.html 文件作为入口,dist 目录存放着所有构建好的文件,均为系统自动生成。在 index.html 文件中,从ember/dist/assets 目录加载了2个脚本文件和2个样式文件:

<!-- ember/dist/index.html -->

<!DOCTYPE html>
<html>
  <head>
    
    <!-- 其他head标签 -->

    <link rel="stylesheet" href="assets/vendor.css">
    <link rel="stylesheet" href="assets/ember-app.css">
    
  </head>
  <body>
    
    <script src="assets/vendor.js"></script>
    <script src="assets/ember-app.js"></script>
    
  </body>
</html>

这4个文件包含了所有前端的逻辑和样式。而 laravel 以 laravel/public 作为项目根目录,该目录下保存了由 laravel 构建好的前端资源。所以我的处理方式如下:

  1. 同步 ember/dist/assets 与 laravel/public/assets 两个目录,后者是前者的镜像

  2. 在 laravel/resources/views 定义一个 view 命名为 app.php,它的内容是 ember/dist/index.html 的拷贝

  3. laravel 拿到除 API 与 AUTH 之外的请求(之后统称非API请求),均返回 app.php

在正常使用时,前端只在首次加载时发出非API请求,一旦拿到 app.php 前端就获得了对应用表现层的控制,只要不刷新页面,之后用户与应用的所有交互都将由前端捕捉与控制。

具体操作如下:

由于我在 windows 下做开发,系统不提供直接同步两个本地目录的工具, 而且也没有找到实时自动同步的第三方桌面应用,最后选择了名为 InSync 的一款软件,每次同步都需要手动点击一下,是一个潜在的效率瓶颈。

在 laravel/resources/views 目录下创建 app.php 文件, 将 ember/dist/index.html 的内容拷贝过来。

在 laravel/app/Http/routes.php 中创建一个新的路由分组:

// laravel/app/Http/routes.php

Route::get('{data?}', function()
{
    return View::make('app');
})->where('data', '.*');

该分组捕捉所有非API请求并返回 app.php。


前端具体实现

在Ember中,每个路由都有与之相关联的一个模型(Model)。Model 负责数据的查询、更改和将更改保存回服务器,这一过程是通过模型适配器(Adapter)完成的。所以需要修改适配器让它匹配后端所定义的 API 前缀约定:

// ember/app/adapters/application.js

export default DS.RESTAdapter.extend({
    namespace: 'api/v1'
});

然后就可以在 ember/app/routers.js 中定义前端路由了:

// ember/app/routers.js

Router.map(function() {

    this.route('user', { path: '/user/:user_id' });

    // Other routes ...

});

这里有个不得不提的问题:

Ember 中每一个 Model 可以视为一种资源,而 Model 已经定义好了与这种资源的各种交互行为。例如当我定义好 userModel 之后,我要向服务器查询一条 user 记录可以使用如下代码,注释给出了它的网络请求(省略了前缀):

this.store.find('user', 5);    // => GET '/users/5'

新建一个用户:

var user = this.store.createRecord('user', {  
    email: '123@123.com',  
    password: '123'
});

user.save();  // => POST to '/users'

这一默认行为是不可配置的,所以后端提供的 API 必须配合该规则进行构建,这也是使用大型框架所带来的灵活性的的缺失。在需要大量定制化功能的应用中,轻量级的前端框架例如 backbone 更具有竞争力。

Ember意识到了这个问题,在最新的2.0版本中,可以通过自定义服务(Service)来解决。


总结

至此,确定了页面加载方案,打通了前后端的数据交互通道,前后端由各自为政变成了相互协作、各司其职,应用终于“活”了起来。


© 著作权归作者所有

Lee的白板报
粉丝 94
博文 31
码字总数 45338
作品 0
高级程序员
私信 提问
加载中

评论(1)

bochs
bochs
可尝试完全将 laravel 和 ember前端 分离,并不一定要将 ember前端 集成到 laravel 中。这样即将 laravel 完全作为服务端,
而将前端视作 APP端 一样了,前端与服务端完全经由 API 通信。
基于 Laravel (5.1) & Ember.js (1.13.0) 的用户授权系统

Laravel 本身提供了完整的用户授权解决方案,对于由 PHP 驱动的多页面应用,Laravel 能够完美解决用户授权问题。但是在 SPA 中,laravel 退化成一个 API server,页面路由和表单提交完全由前...

Lee的白板报
2015/08/16
0
0
Ember.js 1.13.0/2.0 Beta 发布

Ember是一个雄心勃勃的Web应用程序,消除了样板,并提供了一个标准的应用程序架构的JavaScript框架。 Ember.js 1.13.0 和 Ember.js 2.0 Beta 发布。Ember.js 1.13.0 至少有 43 名贡献者参与,...

oschina
2015/06/14
1K
5
Laravel 不权威导航

Laravel不权威导航 Hi 这里是Roy整理的Laravel相关索引,希望能帮到大家 ,目前只是一小部分,正在努力整理中... Laravel 文档 Laravel 官方文档 —— 英文 Laravel 中文文档 —— laravel-...

weixingo
2016/02/05
180
0
Flarum 技术架构 Architecture

Flarum 使用三层体系结构。 1、核心层 - core 负责管理数据、事件、实体、命令等。 2、中间层 - API 使用 Web API 来操作 Flarum。 3、最外层 - Web App 使用 Ember.js 展示数据。 这样的分层...

justjavac
2014/12/31
375
0
Laravel 5.1 源码阅读

安装,和创建项目,都是通过Composer,简单,略过。 Entry && Kernel 网站入口文件,${Laravel-project}/public/index.PHP: 生成Request,处理Request(),生成Response,发送Resonse。常规...

botkenni
2016/10/09
25
0

没有更多内容

加载失败,请刷新页面

加载更多

高并发场景下的缓存有哪些常见的问题?

一、缓存一致性问题 当数据时效性要求很高时,需要保证缓存中的数据与数据库中的保持一致,而且需要保证缓存节点和副本中的数据也保持一致,不能出现差异现象。 这就比较依赖缓存的过期和更新...

别打我会飞
15分钟前
1
0
List list = new ArrayList()为何父类引用指向子类对象(多态)

态:要有继承,方法的重写,父类引用指向子类对象 疑问一:父类引用指向子类对象 与指向父类对象 Animal cat = new Cat(); //向上转型。 父类引用指向子类对象,该引用不能再访问子类新增加的...

architect刘源源
15分钟前
0
0
分而治之-快速排序

快速排序的思想: 快速排序首先在数组中确定1个枢纽项(比如数组中的第一个元素),将大于该枢纽项的元素放到右侧,小于该枢纽项的元素放到左侧,这样枢纽项将数组划分成两部分。接着继续对划...

万山红遍
今天
4
0
Qt编写自定义控件9-导航按钮控件

前言 导航按钮控件,主要用于各种漂亮精美的导航条,我们经常在web中看到导航条都非常精美,都是html+css+js实现的,还自带动画过度效果,Qt提供的qss其实也是无敌的,支持基本上所有的CSS2属...

飞扬青云
今天
3
0
Python开发工具:pyJasper

原文:https://www.oschina.net/p/pyjasper 前言 pyJasper是 JasperReports 网络服务器的 Python 客户端。 pyJasper 是一组 Python 基础工具,可以用来处理 JasperReports 报表 。因为 Jasper...

A_裙232550246
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部