三、建立图像共享网站
有了这一章,我们要创建一个照片分享网站。首先,我们将创建一个图像表。然后我们将介绍调整大小和共享图像的方法。
本章涵盖以下主题:
- 创建数据库并迁移图像表
- 创建照片模型
- 设置自定义配置值
- 安装第三方库
- 为文件上传创建安全表单
- 验证和处理表单
- 用用户界面显示图像
- 列出图像
- 从数据库和服务器中删除映像
创建数据库并迁移图像表
成功安装 Laravel 4 并从app/config/database.php
定义数据库凭证后,创建一个名为images
的数据库。为此,您可以从托管提供商的面板创建一个新的数据库,或者如果您是服务器管理员,您可以简单地运行以下 SQL 命令:
CREATE DATABASE images
为应用成功创建数据库后,我们需要创建一个photos
表并将其安装到数据库中。为此,请打开您的终端,导航到项目文件夹并运行以下命令:
php artisan migrate:make create_photos_table --table=photos –create
这个命令将生成一个新的 MySQL 数据库迁移,为我们创建一个名为 photos 的表。
现在我们需要定义数据库表中应该有哪些部分。举个例子,我觉得id column
、image titles
、image file names
、timestamps
应该就够了。为此,请打开用前面的命令刚刚创建的迁移文件,并更改其内容,如下面的代码所示:
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreatePhotosTable extends Migration {
/**
* Run the migrations.
* @return void
*/
public function up()
{
Schema::create('photos', function(Blueprint $table)
{
$table->increments('id');
$table->string('title',400)->default('');//the column that holds the image's name
$table->string('image',400)->default('');//the column that holds the image's filename
$table->timestamps();
});
}
/**
* Reverse the migrations.
* @return void
*/
public function down()
{
Schema::drop('photos');
}
}
保存文件后,运行以下命令执行迁移:
php artsian migrate
如果没有出现错误,您就可以开始项目的下一步了。
创建照片模型
正如你所知,对于任何与 Laravel 上的数据库操作相关的事情,使用模型是最好的做法。我们将利用 雄辩的 ORM 。
将以下代码保存为app/models/
目录中的images.php
:
<?php
class Photo extends Eloquent {
//the variable that sets the table name
protected $table = 'photos';
//the variable that sets which columns can be edited
protected $fillable = array('title','image');
//The variable which enables or disables the Laravel'stimestamps option. Default is true. We're leaving this hereanyways
public $timestamps = true;
}
我们已经用protected $table
变量设置了表名。表中哪些列可以更新/插入的内容将由protected $fillable
变量决定。最后,模型是否可以添加/更新时间戳将由public $timestamps
变量的值决定。仅仅通过设置这个模型(甚至不设置任何东西),我们就可以轻松地利用雄辩 ORM 的所有优势。
我们的模型已经准备好了,现在我们可以进行下一步,开始创建我们的控制器和上传表单。但在此之前,我们错过了一件简单的事情。图片应该上传到哪里?缩略图的最大宽度和高度是多少?为了设置这些配置值(就像原始 PHP 的常量一样),我们应该创建一个新的配置文件。
设置自定义配置值
使用 Laravel,设置配置值相当容易。所有config
值都在一个数组中,并将被定义为一对key=>value
。
现在让我们创建一个新的配置文件。将该文件保存为app/config
中的image.php
:
<?php
/**
* app/config/image.php
*/
return array(
//the folder that will hold original uploaded images
'upload_folder' => 'uploads',
//the folder that will hold thumbnails
'thumb_folder' => 'uploads/thumbs',
//width of the resized thumbnail
'thumb_width' => 320,
//height of the resized thumbnail
'thumb_height' => 240
);
您可以根据需要设置任何其他设置。那仅限于你的想象。可以用 Laravel 内置的Config
库的get()
方法调用设置。示例用法如以下代码所示:
Config::get('filename.key')
参数之间有一个点(.
),将字符串拆分为两个。第一部分是Config
的文件名,不带扩展名,第二部分是配置值的键名。在我们的示例中,如果我们想要识别哪个文件夹是上传的文件夹名称,我们应该按照下面的代码编写它:
Config::get('image.upload_folder')
前面的代码将返回任何值。在我们的示例中,它将返回public
/ uploads
。
还有一件事:我们为我们的应用定义了一些文件夹名称,但我们没有创建它们。对于某些服务器配置,文件夹可能会在第一次尝试上载文件时自动创建,但如果不创建,很可能会导致服务器配置出错。在public
文件夹中创建以下文件夹,并使其可写:
uploads/
uploads/thumbs
现在我们应该为我们的图片网站制作一个上传表单。
安装第三方库
我们应该制作一个上传表格,然后为我们的图像网站制作一个控制器。但是在此之前,我们将安装一个第三方图像处理库,因为我们将从它的方法中受益。Laravel 4 使用 Composer ,所以安装包、更新包,甚至更新 Laravel 都相当容易。对于我们的项目,我们将使用名为Intervention
的库。安装库必须遵循以下步骤:
- 首先,通过在你的终端运行
php composer.phar self-update
,确保你有最新的composer.phar
文件。 -
Then open
composer.json
and add a new value to therequire
section. The value for our library isintervention/image: "dev-master"
.目前,我们的
composer.json
文件的require
部分如下:php "require": { "laravel/framework": "4.0.*", "intervention/image": "dev-master" }
你可以在www.packagist.org找到更多的作曲家包。
-
After setting the value, open your terminal, navigate to the project's
root
folder, and type the following command:```php php composer.phar update
```
该命令将检查
composer.json
并更新所有依赖项(包括 Laravel 本身),如果添加了新的需求,它将下载并安装它们。 -
成功下载库后,我们现在将激活它。对此,我们参考
Intervention
班的网站。现在打开你的app/config/app.php
,在providers
键上加上以下数值:php Intervention\Image\ImageServiceProvider
-
现在,我们需要设置一个别名,这样我们就可以轻松地调用该类。为此,将以下值添加到同一文件的别名键中:
php 'Image' => 'Intervention\Image\Facades\Image',
-
这个类有一个很容易理解的符号。要调整图像大小,运行以下代码就足够了:
php Image::make(Input::file('photo')->getRealPath())->resize(300, 200)->save('foo.jpg');
注
有关Intervention
课程的更多信息,请访问以下网址:
http://intervention.olivervogel.net
现在,视图和表单处理的一切都准备好了;我们可以继续我们项目的下一步。
为文件上传创建安全表单
现在我们应该为我们的图片网站制作一个上传表单。我们必须创建一个视图文件,它将通过控制器加载。
-
First, open up
app/routes.php
, remove the line starting withRoute::get()
that comes with Laravel, and add the following lines:php //This is for the get event of the index page Route::get('/',array('as'=>'index_page','uses'=>'ImageController@getIndex')); //This is for the post event of the index.page Route::post('/',array('as'=>'index_page_post','before' =>'csrf', 'uses'=>'ImageController@postIndex'));
按键
'as'
定义路线的名称(像快捷方式一样)。因此,如果您链接到路线,即使路线的网址发生变化,您到应用的链接也不会中断。before
键定义在动作开始前将使用什么过滤器。您可以定义自己的过滤器,或者使用内置的过滤器。我们设置了csrf
,所以CSRFT6(跨站点请求伪造)检查将在行动开始前完成。这样,您可以防止攻击者向您的应用中注入未经授权的请求。您可以对分隔符使用多个筛选器;例如filter1|filter2
。注
您也可以直接从控制器定义 CSRF 保护。
-
Now, let's create our first method for the controller. Add a new file containing the following code and name it
ImageController.php
inapp/controllers/
:```php <?php
class ImageController extends BaseController {
public function getIndex() { //Let's load the form view return View::make('tpl.index'); }
} ```
我们的控制器是 RESTful 这就是为什么我们的方法索引被命名为
getIndex()
。在这个方法中,我们只是加载一个视图。 -
Now let's create a master page for the view using the following code. Save this file as
frontend_master.blade.php
inapp/views/
:```php <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
Your Awesome Image Sharing Website
{{--If there is an error flashdata in session(from form validation), we show the first one--}} @if(Session::has('errors')) <h3 class="error">{{$errors->first()}}</h3> @endif {{--If there is an error flashdata in session whichis set manually, we will show it--}} @if(Session::has('error')) <h3 class="error">{{Session::get('error')}}</h3> @endif {{--If we have a success message to show, we printit--}} @if(Session::has('success')) <h3 class="error">{{Session::get('success')}}</h3> @endif {{--We yield (get the contents of) the section named'content' from the view files--}} @yield('content')
```
要添加一个
CSS
文件(我们将在接下来的步骤中创建),我们使用HTML
类的style()
方法。我们的主页产生了一个名为content
的部分,它将被view files
部分填充。 -
Now, let's create our
view file
section by using the following code. Save this file asindex.blade.php
in theapp/views/tpl/
directory:```php @extends('frontend_master')
@section('content') {{Form::open(array('url' => '/', 'files' => true))}} {{Form::text('title','',array('placeholder'=>'Please insert your title here'))}} {{Form::file('image')}} {{Form::submit('save!',array('name'=>'send'))}} {{Form::close()}} @stop ```
在前面代码的第一行,我们告诉 Blade Engine 我们将使用
frontend_master.blade.php
作为布局。这是使用 Laravel 4 中的@extends()
方法完成的。注
如果你来自 Laravel 3,
@layout
改名为@extends
。得益于 Laravel 的
Form
类,我们生成了一个带有title
字段和upload
字段的上传表单。与 Laravel 3 不同,为了制作新的上传表单,我们不再使用Form::open_for_files()
了。它与open()
方法合并,如果你想传递多个参数,它接受一个字符串或者一个数组。我们将传递动作 URL,并告诉它这是一个上传表单,所以我们传递了两个参数。url
键是定义表单提交的位置。files
参数是布尔型的,如果设置为true
,会使表单成为上传表单,这样我们就可以处理文件了。为了保护表单并防止不必要的表单提交尝试,我们需要表单中的 CSRF 密钥
hidden
。由于 Laravel 的Form
类,它是在表单中自动生成的,就在表单开始标记之后。您可以通过查看生成的表单的源代码来检查它。隐藏的自动生成的 CSRF 表单元素如下所示:
php <input name="_token" type="hidden" value="SnRocsQQlOnqEDH45ewP2GLxPFUy5eH4RyLzeKm3">
-
Now let's tidy up the form a bit. This is not directly related to our project, but just for the look. Save the
styles.css
file inpublic/css/
(the path we defined on the master page):php /*Body adjustment*/ body{width:60%; margin:auto; background:#dedede} /*The title*/ h2{font-size:40px; text-align:center; font-family:Tahoma,Arial,sans-serif} /*Sub title (success and error messages)*/ h3{font-size:25px; border-radius:4px; font-family:Tahoma,Arial,sans-serif; text-align:center;width:100%} h3.error{border:3px solid #d00; background-color:#f66; color:#d00 } h3.success{border:3px solid #0d0; background-color:#0f0; color:#0d0}p{font-size:25px; font-weight: bold; color: black;font-family: Tahoma,Arial,sans-serif}ul{float:left;width:100%;list-style:none}li{float:left;margin-right:10px} /*For the input files of the form*/ input{float:left; width:100%; border-radius:13px;font-size:20px; height:30px; border:10px 0 10px 0;margin-bottom:20px}
我们为车身设计了 60%的宽度,使其居中对齐,并赋予其灰色背景。我们还用
success
和error
类以及forms
格式化了h2
和h3
消息。样式化后,表单将如下图所示:
既然我们的表单已经准备好了,我们就可以进入项目的下一步了。
验证和处理表单
在本节中,我们将验证提交的表单,并确保必填字段存在,并且提交的文件是图像。然后我们将图像上传到我们的服务器,处理图像,创建缩略图,并将图像信息保存到数据库,如下所示:
-
First, we need to define the form validation rules. We prefer adding such values to the related model, so the rules become reusable, and this prevents the code from becoming bloated. To do this, add the following code in the
photo.php
file in theapp/models/
directory (the model that we generated earlier in this chapter) inside the class definition before the last curly bracket(}
):php //rules of the image upload form public static $upload_rules = array( 'title'=> 'required|min:3', 'image'=> 'required|image' );
我们把变量设置为
public
,这样就可以在模型文件之外使用,我们把它设置为静态,这样就可以直接访问变量了。我们希望
title
和image
都是强制性的,title
至少要有三个字符。此外,我们要检查image
列的 MIME 类型,并确保它是图像。注
Laravel 的 MIME 类型检查需要安装
Fileinfo
扩展。因此,请确保在您的 PHP 配置中启用了它。 -
Now we need the controller's
post
method to process the form. Add this method in theImageController.php
file inapp/controllers/
before the last curly bracket(}
):```php public function postIndex() {
//Let's validate the form first with the rules which areset at the model $validation = Validator::make(Input::all(),Photo::$upload_rules);
//If the validation fails, we redirect the user to theindex page, with the error messages if($validation->fails()) { return Redirect::to('/')->withInput()->withErrors($validation); } else {
//If the validation passes, we upload the image to thedatabase and process it $image = Input::file('image'); //This is the original uploaded client name of theimage $filename = $image->getClientOriginalName(); //Because Symfony API does not provide filename//without extension, we will be using raw PHP here $filename = pathinfo($filename, PATHINFO_FILENAME); //We should salt and make an url-friendly version of//the filename //(In ideal application, you should check the filename//to be unique) $fullname = Str::slug(Str::random(8).$filename).'.'.$image->getClientOriginalExtension(); //We upload the image first to the upload folder, thenget make a thumbnail from the uploaded image $upload = $image->move(Config::get( 'image.upload_folder'),$fullname); //Our model that we've created is named Photo, thislibrary has an alias named Image, don't mix them two! //These parameters are related to the image processingclass that we've included, not really related toLaravel Image::make(Config::get( 'image.upload_folder').'/'.$fullname)->resize(Config::get( 'image.thumb_width'),null, true)->save(Config::get( 'image.thumb_folder').'/'.$fullname); //If the file is now uploaded, we show an error messageto the user, else we add a new column to the databaseand show the success message if($upload) { //image is now uploaded, we first need to add columnto the database $insert_id = DB::table('photos')->insertGetId( array( 'title' => Input::get('title'), 'image' => $fullname ) ); //Now we redirect to the image's permalink return Redirect::to(URL::to('snatch/'.$insert_id))->with('success','Your image is uploadedsuccessfully!'); } else { //image cannot be uploaded return Redirect::to('/')->withInput()->with('error','Sorry, the image could not beuploaded, please try again later'); }
} } ```
让我们一个一个地挖掘代码。
- 首先,我们进行表单验证,并从我们通过
Photo::$upload_rules
生成的模型中调用我们的验证规则。 - 然后我们给文件名加盐(为了安全添加了额外的随机字符),并使文件名对网址友好。首先,我们用 getClientOriginalName()方法获取上传的文件名,然后用 getclientoriginalextrance()方法获取扩展名。我们用一个八个字符长的随机字符串给文件名加盐,该字符串是通过 STR 类的 random()方法获得的。最后,我们用 STR 类的 Laravel 内置 slug()方法使文件名对 URL 友好。
- 在所有变量准备好之后,我们首先用 move()方法将文件上传到服务器,这个方法需要两个参数。第一个参数是文件将要传输到的路径,第二个参数是上传文件的文件名。
- 上传后,我们为上传的图像创建了一个静态缩略图。为此,我们受益于我们之前实现的图像处理类 Intervention。
- 最后,如果一切顺利,我们将标题和图像文件名添加到数据库中,并使用 Fluent 查询生成器的 insertGetId()方法获取标识,该方法首先插入行,然后返回列的 insert_id。我们还可以通过将 create()方法设置为一个变量,并获取 id_column 名称,如$create- > id,来用雄辩的 ORM 创建行。
- 在一切正常并获得
insert_id
后,我们将用户重定向到一个新页面,该页面将显示缩略图、全图像链接和论坛缩略图 BBCode ,我们将在接下来的部分中生成该页面。
- 首先,我们进行表单验证,并从我们通过
用用户界面显示图像
现在,我们需要从控制器中制作一个新的视图和方法来显示上传图像的信息。这可以通过以下方式实现:
-
First, we need to define a
GET
route for the controller. For this, open your fileroutes.php
in theapp
folder and add the following codes:php //This is to show the image's permalink on our website Route::get('snatch/{id}', array('as'=>'get_image_information', 'uses'=>'ImageController@getSnatch')) ->where('id', '[0-9]+');
我们在路线上定义了一个
id
变量,用where()
方法,使用正则表达式,我们第一手过滤了它。所以我们不需要担心过滤 ID 字段,不管是不是自然数。 -
Now, let's create our controller method. Add the following code inside
ImageController.php
inapp/controllers/
before the last curly bracket (}
):php public function getSnatch($id) { //Let's try to find the image from database first $image = Photo::find($id); //If found, we load the view and pass the image info asparameter, else we redirect to main page with errormessage if($image) { return View::make('tpl.permalink')->with('image',$image); } else { return Redirect::to('/')->with('error','Image not found'); } }
首先,我们用雄辩术的
find()
方法寻找图像。如果它返回的值为 false,则意味着找到了一行。所以我们可以简单的用一个简单的if
子句来检查是否有结果。如果有结果,我们将使用with()
方法将找到的图像信息作为名为$image
的变量加载到视图中。如果没有找到任何值,我们将返回到索引页,并显示一条错误消息。 -
Now let's create the template file containing the following code. Save this file as
permalink.blade.php
inapp/views/tpl/
:```php @extends('frontend_master') @section('content')
Title: {{$image->title}}
{{HTML::image(Config::get('image.thumb_folder').'/'.$image->image)}}Direct Image URL
<p>Thumbnail Forum BBCode</p> <input onclick="this.select()" type="text"width="100percent" value="[url={{URL::to('snatch/'$image->id)}}][img]{{URL::to(Config::get('image.thumb_folder')'/'.$image->image)}}[/img][/url]" /> <p>Thumbnail HTML Code</p> <input onclick="this.select()" type="text"width="100percent"value="{{HTML::entities(HTML::link(URL::to('snatch/'.$image->id),HTML::image(Config::get('image.thumb_folder').'/'$image->image)))}}" /> </td>
到目前为止,您应该已经熟悉了该模板中使用的大多数方法。有一个新的方法叫做
HTML
类的entities()
,这个实际上是原始 PHP 的htmlentities()
,但是带有一些预检查,并且是 Laravel 的方式。此外,因为我们已经将
$image
变量返回到视图中(这是我们使用雄辩获得的数据库行对象),所以我们可以在视图中将其直接用作$image->columnName
。这将产生如下图所示的视图:
-
我们已经为我们的项目增加了一个永久链接功能,但是如果我们想显示所有的图像呢?为此,我们需要在系统中有一个
'all pages'
部分。
列出图像
在这一部分,我们将在我们的系统中创建一个'all images'
部分,它将有一个页面导航(分页)系统。如图所示,需要遵循几个步骤:
-
首先,我们需要从我们的
route.php
文件中定义它的 URL。为此,打开app/routes.php
并添加以下行:php //This route is to show all images. Route::get('all',array('as'=>'all_images','uses'=>'ImageController@getAll'));
-
Now, we need a method named
getAll()
(there is aget
method at the start because it will be a RESTful controller) to get values and load the view. To do this, open yourapp/controllers/ImageController.php
and add these codes before the last curly bracket (}):```php public function getAll(){
//Let's first take all images with a pagination feature $all_images = DB::table('photos')->orderBy('id','desc')->paginate(6);
//Then let's load the view with found data and pass thevariable to the view return View::make('tpl.all_images')->with('images',$all_images); } ```
在这里,我们首先使用
paginate()
方法从数据库中获取所有图像,这将允许我们轻松获取分页链接。之后,我们为用户加载了带有分页的图像数据视图。 -
To view this properly, we need a view file. Save the following code in a file named
all_image.blade.php
in theapp/views/tpl/
directory:```php @extends('frontend_master')
@section('content')
@if(count($images))
@foreach($images as $each) <li> <a href="{{URL::to('snatch/'$each->id)}}">{{HTML::image(Config::get('image.thumb_folder')'/'.$each->image)}}</a> </li> @endforeach
{{$images->links()}}
@else {{--If no images are found on the database, we will showa no image found error message--}}No images uploaded yet, {{HTML::link('/','care to upload one?')}}
@endif @stop ```我们首先用我们的内容部分扩展
frontend_master.blade.php
文件。至于内容部分,我们首先检查是否返回了任何行。如果是这样,那么我们用它们的永久链接在列表项标签(<li>
)中循环它们。paginate
类附带的links()
方法将为我们创建分页。注
可以从
app/config/view.php
切换分页模板。如果没有返回任何行,这意味着没有图像(还没有),所以我们显示一条警告消息,其中包含指向新上传页面(在我们的例子中是索引页面)的链接。
如果一个人上传了一张不允许或者不安全的工作图片怎么办?你不希望他们出现在你的网站上,对吗?所以在你的网站上应该有一个图片删除功能。
从数据库和服务器中删除图像
我们希望在我们的脚本中有一个删除功能,使用它我们将从数据库和上传的文件夹中删除图像。这个过程对 Laravel 来说相当容易。
-
首先,我们需要为行动创建一条新路线。为此,打开
app/routes.php
并添加以下行:php //This route is to delete the image with given ID Route::get('delete/{id}', array ('as'=>'delete_image','uses'=> 'ImageController@getDelete')) ->where('id', '[0-9]+');
-
Now, we need to define the controller method
getDelete($id)
insideImageController
. To do this, openapp/controllers/ImageController.php
and add the following code above the last curly bracket (}
):```php public function getDelete($id) { //Let's first find the image $image = Photo::find($id);
//If there's an image, we will continue to the deletingprocess if($image) { //First, let's delete the images from FTP File::delete(Config::get('image.upload_folder').'/'$image->image); File::delete(Config::get('image.thumb_folder').'/'$image->image);
//Now let's delete the value from database $image->delete(); //Let's return to the main page with a success message return Redirect::to('/')->with('success','Image deleted successfully');
} else { //Image not found, so we will redirect to the indexpage with an error message flash data. return Redirect::to('/')->with('error','No image with given ID found'); } } ```
让我们理解代码:
- 首先,我们看一下我们的数据库,如果我们已经有一个带有给定标识的图像,并且已经使用了雄辩 ORM 的
find()
方法,我们将使用一个名为$image
的变量来存储它。 - 如果
$image
的值不为假,则在我们的数据库中有一个与该图像匹配的图像。然后,我们用文件类的delete()
方法删除文件。或者,也可以使用原 PHP 的 unlink()方法。 - 从文件服务器中删除文件后,我们从数据库中删除图像的信息行。为此,我们使用了雄辩表单的
delete()
方法。 - 如果一切顺利,我们应该重定向回主页面,并显示一条成功消息,表示图像删除成功。
注
在实际应用中,您应该有一个用于此类操作的后端接口。
- 首先,我们看一下我们的数据库,如果我们已经有一个带有给定标识的图像,并且已经使用了雄辩 ORM 的
总结
在这一章中,我们创建了一个简单的图像共享网站,内置了 Laravel 的功能。我们已经学习了如何验证表单,如何处理文件并检查其 MIME 类型,以及设置自定义配置值。我们已经通过 Fluent 和 ORM 了解了更多关于数据库方法的知识。此外,对于图像处理,我们使用 Composer 安装了来自packagist.org的第三方库,并学习了如何更新它们。我们还列出了带有页面导航功能的图像,并学会了从服务器上删除文件。在下一章中,我们将构建一个带有身份验证和仅限成员区域的个人博客网站,并将博客文章分配给作者。
版权属于:月萌API www.moonapi.com,转载请注明出处