引言:
本文基于Laravel
框架做的一个URL生成和存储demo,主要目的是学习使用Laravel
框架。内容基于英文书籍《Packt.Laravel.Application.Development.Blueprints》
第一章节,似乎没有中文版。书籍基于Laravel4
的,学习时使用Laravel5.2
框架开发。
注明:作者水平有限,有错误或建议请指正,轻拍。
学习主题
该demo主要涉及如下几个知识点:
创建数据库并迁移数据表
创建表单,学习
Laravel
的blade
模板引擎创建名为Link的模型
Model
保存数据进入数据库
从数据库中获得URL链接并重定向
1、创建数据库并迁移数据表单
表迁移(Migrations
)其实就是数据库(Database
)的版本控制,允许团队修改数据库架构,并保存当前数据库最新架构信息,为了创建并迁移创建的URL数据库,需要做几个步骤:(1)
、首先创建一个数据库并定义该数据库与Laravel
的连接信息,Laravel
框架为数据库连接提供了配置文件:/config/database.php
,Laravel
支持SQlite、MySQL、PostgreSQL、SQLSRV
数据库,本教程使用MySQL
数据库。(2)
、创建一个名为urls
的数据库,终端登入mysql服务器或者使用phpMyAdmin敲入SQL语句:CREATE DATABASE urls
。可以使用PHPStorm
这个IDE的database
模块远程创建urls
数据库。(3)
、在成功创建数据库urls后,开始配置数据库名称和用户名密码,在/config/database.php
里配置host,database,username,password
,由于配置文件使用env()函数先读取.env文件里的值,所以可以直接在.env文件里配置。.env文件可以从.env.example文件复制得来,如修改.env文件中值:DB_HOST=localhost,DB_DATABASE=urls,DB_USERNAME=root,DB_PASSWORD=laravel
;也可以修改/config/database.php
文件为:'host'=>'localhost','database'=> 'urls','username'=>'root','password' =>'laravel'
。数据库用户名为安装MySQL时配置的。(4)
、使用Laravel
的Artisan CLI
工具命令创建migrations迁移文件,可以在终端进入项目根目录输入php artisan
命令查看Artisan命令列表。现在创建一个名为links的迁移文件:php artisan make:migration create_links_table --create=links
,会在/database/migrations/
文件夹下新建一个date+create_links_table.php
文件,该文件源码主要包含两个非常重要的方法:up()/down()
。当执行数据表迁移命令php artisan migrate
时执行的是up()
方法;当执行回滚上一次迁移命令php artisan migrate:rollback
时执行的是down()
方法,该命令具有破坏性会删除links数据表。曾经遇到一个场景:需要给数据表test增加一个字段age但又要保留test表里数据,可以再创建一个迁移文件php artisan make:migration create_links_table --table=links
,生成的迁移文件中up()
方法里引用了Schema::table()
方法而不是Schema::create()
方法,再添加$table->string('age')->default(0);
语句,删除原来的'id'和timestamps邮戳语句,再执行php artisan migrate
迁移命令。(5)
、在创建的迁移文件内增加两个字段:$table->text('url');$table->string('hash',400);
,自动生成的$table->timestamps();
语句是给数据表添加邮戳,会自动增加'created_at'/'updated_at'
两个字段,默认时间是UTC与北京时间相差8小时,可以在config/app.php
文件中修改'timezone'
值为'Asia/Shanghai'
就行。再执行迁移命令php artisan migrate
,如果数据库配置没有错误就会在urls数据库里生成links表和migrations表,migrations表记录的是迁移记录可不用管。
注明:可以安装phpstorm
这个IDE,使用它的database模块查看数据库,说实话个人用的感觉还挺顺手的,当然也可以安装navicat premium
软件查看。
2、创建Form表单
(1)
、在resources/views/
文件夹下创建一个urls
文件夹,在urls
文件夹下创建一个form.blade.php
文件文件名需要有blade字符串,laravel会自动识别这个文件为blade模板文件。form模板代码为:
<html lang="en">
<head>
<title>URL Shortener</title>
<link rel="stylesheet" href="{{asset('css/urls/style.css')}}">
</head>
<body>
<div id="container">
<h2>短链接生成器</h2>
{{Form::open(array('url'=>'/url','method'=>'post'))}}
{{Form::text('link', '请输入您的网址!')}}
{{--{{Form::text('link',Input::old('link'), array('placeholder'=>'请输入您的网址!'))}}--}}
{{Form::close()}}
</div>
</body>
</html>
注明
:我的Laravel代码会报错没有Input这个类,很奇怪,那就把这一句注销掉吧。如果你知道咋解决,请在评论留言给我,谢谢
。样式style.css文件可以用asset()函数在public文件夹下找到路径,在这里就是public/css/urls/style.css
。当然,也可以不用这个Form类,直接写<form></form>表单html代码也行。这里的url表示提交表单时的路由,方法为post。在这里使用laravelcollective/html这个组件,顺便了解下怎么在laravel中安装组件。
这里书中使用了laravel4.*
自带的Form类,但laravel5.*
已经移除了,可以通过composer
Composer官网安装。可以进入官网https://laravelcollective.com/docs/5.1/html
找安装和配置方式,也可以去packagist.org
中找https://packagist.org/packages/laravelcollective/html
,这里推荐一个非常好用的网站packagist
,PHP中所有组件components都可以在这里找到并通过composer安装。《Modern PHP》(可以去pan.java1234.com
搜到英文版的)这本书中倡导PHP软件开发应该使用组件components方式来做,利用别人的轮子会加速开发效率,组件components可装可卸,组件式代码比面条式代码读起来更舒服!!!
通过composer安装也很简单,就是在项目根目录下的composer.json
文件'require'数组中添加"laravelcollective/html": "5.2.*",
,再composer update
就行,安装完laravelcollective/html后在config/app.php
文件中配置这个组件的服务serviceprovider,在'providers'数组中加上CollectiveHtmlHtmlServiceProvider::class,
,在'aliases'数组中加上'Form' => CollectiveHtmlFormFacade::class,'Html' => CollectiveHtmlHtmlFacade::class,
,就可以用这个组件轮子了,实际上很多组件也都是这么安装配置的。
样式style.css代码为:
div#container{padding-top:100px;text-align:center;width:75%;margin:auto;border-radius:4px}
div#container h2{font-family:Arial,sans-serif;font-size:28px;color:#555}
div#container h3{font-family:Arial,sans-serif;font-size:28px}
div#container h3.error{color:#a00}
div#container h3.success{color:#0a0}
div#container input{display:block;width:90%;float:left;font-size:24px;border-radius:5px}
div#error,div#success{border-radius:3px;display:block;width:90%;padding:10px}
div#error{background:#ff8080;border:1px solid red}
div#success{background:#80ff80;border:1px solid #0f0}
balde模板页面写完,然后在routes.php路由文件中写个路由:
Route::get('/url', function(){
return view('urls.form');//urls为创建的文件夹
});
这里路由第二个参数为匿名函数,直接返回视图,当然可以建个控制器php artisan make:controller UrlController
,在控制器里写个getUrl()方法返回视图,那路由就要这么写了:Route::get('url', 'UrlController@getUrl')
。
最后输入URL:http://yourhost/url
,则blade模板页面如图所示:
3、创建名为Link的Model
Laravel提供了一个非常好用的ORM(Object Relationship Mapping)为Eloquent ORM,其实就是Model层,来管理数据库中的数据表且一一对应关系。Eloquent比较好用在于它提供了很多Feature功能模块,这些模块提供了许多面向对象的方法便于使用,这样就不用写SQL语句了,且代码看起来也很舒服。。不过有时也推荐使用它的Query Builder查询构造器,实际上就是SQL语句封装的类,性能会比较高一些,个人遇到过一个场景:使用Eloquent ORM性能有点慢,导致PHP执行过长报503 Time Out,改成Query Builder后性能高很多脚本执行很快搞定,当然各有利弊,毕竟Eloquent很强大很好用。
在项目根目录执行Artisan命令php artisan make:model Link
后,生成app/Link.php
文件,这个model通过配置用来管理MySQL中的links数据表,在Link这个model里写上配置:
class Link extends Model
{
//
protected $table = 'links';
protected $fillable = ['url', 'hash'];
}
$table变量配置成MySQL中links数据表的名称,$fillable用来配置数据表字段(column)被批量创建和更新的,因为后文在保存数据进入表里时使用Link::create([])方法来进行批量赋值的。可以查看Model这个class源码里有$table和$fillable字段,这个Model类提供了许多好用的方法,有时间可以瞅瞅。如果不需要laravel自动创建的时间可以写上public $timestamps = false;
再执行迁移命令,links数据表里就没有'created_at'/'updated_at'
字段了。这里注意下:如果不写$table变量,laravel会自动根据model名字复数来找数据表,如这个model名字是link,那就找links表。
4、保存数据进入数据库
写好视图表单后,再就是写表单的提交路由及其控制器逻辑,在控制器中引用创建好的Link这个Model往links数据表里存数据。原文书中是直接在路由中匿名函数里写数据存储逻辑,这里个人还是先创建一个控制器php artisan make:controller UrlController
,在控制器里写数据存储逻辑比较好。实际上,控制器也就是路由层route、视图层view与模型model层的黏合剂而已,一般写laravel代码流程也仅此而已:现在路由里写好路由,再建立好model(包括创建好migrations和model,写好数据库连接配置、model配置、执行migrations表迁移),再在控制器controller里写好业务逻辑,返回response如blade视图view或直接一个"hello world"字符串吧,最后要是返回view那就在resources/views
里写个view就行。laravel框架使用也仅此而已,没有那么复杂,对于我们这样的刚刚入门,了解这个流程就可以玩一玩了!!!
(1)
、验证输入
在提交表单时都要验证输入数据是否符合规定,免得让脏数据进入数据表里,laravel提供了Validation模块来做表单验证并且可以在视图中显示验证错误信息,具体想了解下的可以看我这篇文章:Laravel学习笔记之Validator。
在验证表单时首先需要写验证规则$rules,本demo仅有一个输入且输入要符合URL格式,那就要考虑两个问题:怎么得到表单的输入$input和怎么写符合URL的$rules验证规则。首先使用验证方法Validator::make([], [])
,这个方法的第一个参数是取得的表单输入$input,第二个参数是验证规则$rules。demo中只有一个输入可以使用Input::all()
取得或者Input::get('link')
,其中link为这个输入的name,对应表单视图的{{Form::text('link', '请输入您的网址!')}}
这个link
,$rules验证规则这么写:
$rules = array(
'link' => 'required|url'
);
这里'required'是输入不能为空,是laravel自带的验证规则,'url'也是laravel自带的URL验证规则,就是格式得符合URL格式,'|'表示且的意思。
好,现在就按照流程写代码:
首先:
Route::post('url', 'UrlController@postUrl');
然后在UrlController中写上:
public function postUrl(){
$rules = array(
'link' => 'required|url'
);
//$validation = Validator::make(Input::all(),$rules);
$validation = Validator::make(Input::get('link'),$rules);
}
这里这个存储变量$validation存储了很多验证信息,很有用,如验证通过($validation->passes()
)和验证失败($validation->fails()
),这两个函数返回Boolean结果,还有$validation->messages()
函数返回验证失败后验证信息。
如果验证失败,就返回URL表单提交页面,并且带上flash data,flash data是被刷到session中,可以在模板视图view中通过Session::get('XXX')
得到,下面会看到代码。那怎样把flash data数据刷到session中:laravel提供了with()
或withVariableName()
方法,如果直接返回view时,$VariableName直接在模板视图中作为变量使用,如果是重定向view时,需要通过Session::get('VariableName')
方法获取变量值。
比较:
public function getUrl(){
$title = 'Url Generator';
return view('urls.form')->with('titletitle', $title);//blade模板中直接使用$titletitle变量就行,如<title>{{$titletitle}}</title>
}
public function postUrl(){
$title = 'Url Generator';
return Redirect::to('/url')->with('titletitle', $title);//这里重定向页面,在blade模板视图中得到$titletitle变量可以这么做,Session::get('titletitle'),检查有无变量这么做Session::has('titletitle')
}
然后在postUrl()方法中写上验证失败的话重定向URL表单提交页面:
if($validation->fails()) {
return Redirect::to('/url')
->withInput()
->withErrors($validation);
}
这里$errors变量在blade视图模板中可以直接引用就不用Session::get()了,这是因为laravel会自动把这个变量和视图模板绑定,这$errors是个特殊的变量,在form.blade.php视图中添加上验证错误信息代码。withInput()
函数会在返回表单时在input里填上刚刚输入的旧数据。
(2)
、将验证信息传到模板视图中
@if(Session::has('errors'))
<h3 class="error">{{$errors->first('link')}}</h3>
@endif
first()
函数返回link表单的第一个验证错误信息。当然也可以遍历验证信息并显示出来:
@if(Session::has('errors'))
<div class="alert alert-danger">
<ul>
@foreach($errors->all() as $error)
<li>{{$error}}</li>
@endforeach
</ul>
</div>
@endif
5、深度优化控制器并处理表单Form
代码的else
部分主要处理当验证通过后,主要实现以下逻辑:
检查link链接是否已经在数据表里
如果link链接已经在数据表里,返回该短连接
如果link链接不在数据表里,那就为该链接创建一个hash字段
根据提供数据在数据表里插入一个记录record
返回该链接给用户
(1).使用Query Builder的where()方法,并传入Input::get('link')参数验证数据表里是否已经有该链接,并链式使用first()方法取出第一个结果:
$link = Link::where('url','=',Input::get('link'))->first();
(2).如果数据表里有该链接,重定向到表单页面并带上数据表的hash字段:
if($link) {
return Redirect::to('/url')->withInput()->with('link',$link->hash);
}
可以通过$link->columnName取得数据表里的字段值。这里with('name','value')等同于驼峰方法withName('value')方法,上文也说明了。在form.blade视图中也加上消息:
@if(Session::has('link'))
<h3 class="success">
{{Html::link(Session::get('link'),'Click here for your shortened URL')}}//Html类是laravelcollective/html这个模块里的类,或者直接写个`a`超链接标签也行
</h3>
@endif
(3).链接不在数据表里,为该链接创建一个hash字段,原文使用$newHash = Str::random(6)
创建一个包含数字字母的字符长度为6的字符串,再去表里验证该$newHash是唯一的,这样比较麻烦,可以直接使用Hash::make($param)
就行:
else{
$newHash = Hash::make(Input::get('link'));//根据输入的link做hash哈希就行或者别的更简短的输入值
}
(4).向link数据表里插入一个新的记录record:
else{
$newHash = Hash::make(Input::get('link'));//根据输入的link做hash哈希就行或者别的更简短的输入值
Link::create([
'url' => Input::get('link'),
'hash' => $newHash
]);
}
使用Link模型的create()方法创建一条record,或者:
$link = new Link();
$link->url = Input::get('link');
$link->hash = $newHash;
$link->save();
之前研究过一个小点:使用create方式是需要在Link模型类中写上$fillable指定批量赋值字段,否则报错,而这个save方式不需要这么做。
(5).再重定向到表单提交页面
return Redirect::to('/url')
->withInput()
->with('link', $newHash);
这里带上$newHash变量是为了后面捕获这个变量后,根据这个变量从数据表里查找对应的url值。
6、从数据库中取出URL并且重定向
最后根据生成的URL获取其hash部分,根据hash值从links数据表取出对应的URL为了重定向,这里英文原文也是在路由中写逻辑,这里也在路由里写逻辑:
Route::get('/url/{hash}', function($hash){
$link = Link::where('hash',$hash)->first();
if($link){
return Redirect::to($link->url);
}else{
return Redirect::to('/url')->with('message', 'Invalid Link');
}
})->where('hash', '[0-9a-zA-Z+]');
{hash}
是一个路由参数,作为匿名函数的参数,并且正则限制其是由数字字母组合where('hash', '[0-9a-zA-Z+]')
,如果links数据表里有数据就跳转到这个链接去,没有则返回message给blade模板视图,所以form.blade模板视图需要添加显示:
@if(Session::has('message'))
<h3 class="error">{{Session::get('message')}}</h3>
@endif
欢迎关注Laravel-China。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。