1

引言:

本文基于Laravel框架做的一个URL生成和存储demo,主要目的是学习使用Laravel框架。内容基于英文书籍《Packt.Laravel.Application.Development.Blueprints》第一章节,似乎没有中文版。书籍基于Laravel4的,学习时使用Laravel5.2框架开发。
注明:作者水平有限,有错误或建议请指正,轻拍。

学习主题

该demo主要涉及如下几个知识点:

  1. 创建数据库并迁移数据表

  2. 创建表单,学习Laravelblade模板引擎

  3. 创建名为Link的模型Model

  4. 保存数据进入数据库

  5. 从数据库中获得URL链接并重定向

1、创建数据库并迁移数据表单

表迁移(Migrations)其实就是数据库(Database)的版本控制,允许团队修改数据库架构,并保存当前数据库最新架构信息,为了创建并迁移创建的URL数据库,需要做几个步骤:
(1)、首先创建一个数据库并定义该数据库与Laravel的连接信息,Laravel框架为数据库连接提供了配置文件:/config/database.phpLaravel支持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)、使用LaravelArtisan 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.*已经移除了,可以通过composerComposer官网安装。可以进入官网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

RightCapital招聘Laravel DevOps


lx1036
3.1k 声望923 粉丝

为五斗米折腰