文档章节

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

Lee的白板报
 Lee的白板报
发布于 2015/08/15 16:21
字数 2092
阅读 234
收藏 5
点赞 1
评论 1

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的白板报
粉丝 90
博文 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

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 ⋅ 5

Flarum 技术架构 Architecture

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

justjavac ⋅ 2014/12/31 ⋅ 0

Laravel 不权威导航

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

weixingo ⋅ 2016/02/05 ⋅ 0

Laravel5学生成绩管理系统-01-安装-建表-填充数据

注:以下笔记都是按照此博文进行开发学习,开发环境为Mac 。 一、从github上克隆项目到本地 二、切换到本地项目目录下,安装composer // 可以将上边的命令放入到 bin目录下,方便后边不用每次...

Corwien ⋅ 2016/04/21 ⋅ 0

Laravel 5.1 源码阅读

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

botkenni ⋅ 2016/10/09 ⋅ 0

Laravel 5.1 LTS 发布,支持 PSR-2

Laravel 5.1 是 Laravel 首个 LTS 版本,包含很多新特性。 Laravel 5.1 现在开始会包括 3 年的安全修复。此版本也重新编写了文档,而且添加了实时搜索的自动完成功能。 应用和生成器转换为 ...

raykwok ⋅ 2015/06/09 ⋅ 9

Lumen 5.1 发布,微型 PHP 框架

Lumen 5.1 已经发布并可用了 。此版本的特性包括:基于最近发布的laravel 5.1部分组成,遵循PSR-2,以及各种错误修复和改进。基于包含的Laravel5.1组件,也意味着Lumen支持事件广播,模型工厂...

raykwok ⋅ 2015/06/17 ⋅ 8

基于 Laravel 5.1 的 PHPHub Server 端 --phphub-server

本项目是使用 Laravel 5.1 重写的 PHPHub,遵循 Laravel 代码规范。 遇到问题或有好的建议? 欢迎 star 和提交 pr 或提交一个 issue ❤ 目前仅完成了 API 接口,WEB 版尚未在开发中。 环境依...

sikkx ⋅ 2015/12/11 ⋅ 0

Laravel 5.1和5.6用指定字段登陆的用法区别

一般来说,从网上找到的东东,5.1版本,会提示: 使用用户名登录 Laravel默认使用邮箱作为用户名登录,在Laravel 5.1中,可以在中设置属性来指定登录账号选项,该属性默认值是,如果要使用用...

天飞 ⋅ 04/23 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

破解公司回应苹果“USB限制模式”:已攻破

本周四,苹果发表声明称 iOS 中加入了一项名为“USB 限制模式”的功能,可以防止 iPhone 在连接其他设备的时候被破解,并且强调这一功能并不是针对 FBI 等执法部门,为的是保护用户数据安全。...

六库科技 ⋅ 29分钟前 ⋅ 0

MyBtais整合Spring Boot整合,TypeHandler对枚举类(enum)处理

概要 问题描述 我想用枚举类来表示用户当前状态,枚举类由 code 和 msg 组成,但我只想把 code 保存到数据库,查询处理,能知道用户当前状态,这应该怎么做呢?在 Spring 整合MyBatis 的时候...

Wenyi_Feng ⋅ 47分钟前 ⋅ 0

redis安装记录

自行学习记录吧,有来自网络学习过程和调整不是照搬的 第一步:下载redis安装包 wget http://download.redis.io/releases/redis-4.0.10.tar.gz 第二步:解压压缩包 tar -zxvf redis-4.0.10.t...

cavion ⋅ 54分钟前 ⋅ 0

synchronized与Lock的区别

# <center>王梦龙的读书笔记第一篇</center> ## <center>-synchronized与Lock的区别</centre> ###一、从使用场景来说 + synchronized 是能够注释代码块、类、方法但是它的加锁是和解锁使用一......

我不想加班 ⋅ 今天 ⋅ 0

VConsole的使用

手机端控制台打印输出,方便bug的排查。 首先需要引入vconsole.min.js 文件,然后在文件中创造实例。就能直接使用了。 var vConsole = new VConsole(); vConsole的文件地址...

大美琴 ⋅ 今天 ⋅ 0

Java NIO之字符集

1 字符集和编解码的概念 首先,解释一下什么是字符集。顾名思义,就是字符的集合。它的初衷是把现实世界的符号映射为计算机可以理解的字节。比如我创造一个字符集,叫做sex字符集,就包含两个...

士别三日 ⋅ 今天 ⋅ 0

Spring Bean基础

1、Bean之间引用 <!--如果Bean配置在同一个XML文件中,使用local引用--><ref bean="someBean"/><!--如果Bean配置在不同的XML文件中,使用ref引用--><ref local="someBean"/> 其实两种......

霍淇滨 ⋅ 今天 ⋅ 0

05、基于Consul+Upsync+Nginx实现动态负载均衡

1、Consul环境搭建 下载consul_0.7.5_linux_amd64.zip到/usr/local/src目录 cd /usr/local/srcwget https://releases.hashicorp.com/consul/0.7.5/consul_0.7.5_linux_amd64.zip 解压consu......

北岩 ⋅ 今天 ⋅ 0

Webpack 4 api 了解与使用

webpack 最近升级到了 v4.5+版 01 官方不再支持 node4 以下版本 官方不再支持 node4 以下版本官方不再支持 node4 以下的版本,所以如果你的node版本太低,先开始升级node吧!话说node10 ...

NDweb ⋅ 今天 ⋅ 0

数据结构与算法之魔术师发牌问题

一、魔术师发牌问题 1.问题描述 魔术师利用一副牌中的13张黑牌,预先将他们排好后叠放在一起,牌面朝下。对观众说:“我不看牌,只数数就可以猜到每张牌是什么。”魔术师把最上面的那张牌数为...

aibinxiao ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部