使用 Laravel 纯手工打造一个简单的电子商务网站(一) —— 后台新增商品及前台显示

Laravel 电商网站

1、概述

本系列教程我们将演示如何使用 Laravel 5.2 纯手工打造一个简单但功能完备的电商网站。这个电商网站只售卖以ZIP格式打包的电子文章,用户下单并付款后即可下载,有点类似一些售卖网站主题(打包的文件)的网站,比如ThemeForest。

我们将会用到Laravel的一些功能特性,比如存储Storage)和支付(Cashier),我们使用Storage将打包后的文章存储起来并放到网站售卖,而Cashier则用于下单后使用信用卡支付。和世界上其他大大小小的电商网站一样,我们首先要做的事情是上架商品,所以本章节我们将会创建一个可以添加商品的后台以及一个展示商品列表的前台页面。

本系列教程完整代码可以在GitHub上获取:https://github.com/nonfu/laravel-store

2、安装&设置

在此之前我们假设你已经安装好了Laravel并设置好了数据库连接,如果没有的话,请参考安装文档进行安装配置。

然后我们使用 Bootstrap 和 FontAwesome 来创建一个样式美观的布局页面。在layouts目录下创建一个master.blade.php文件,编辑其内容如下:

<html>
<head>
    <title>Laravel 商店</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
    <script src="https://code.jquery.com/jquery-3.0.0.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
    <link rel="stylesheet" href="/css/style.css">
</head>
<body>
@section('sidebar')
    <nav class="navbar navbar-default  navbar-fixed-top" role="navigation">
        <div class="container-fluid">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="/">Laravel 商店</a>
            </div>
            <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                <ul class="nav navbar-nav navbar-right">
                    @if(!Auth::user())
                        <li><a href="/auth/login">登录</a></li>
                        <li><a href="/auth/register">注册</a></li>
                    @else
                        <li><a href="/order">我的订单 <span class="fa fa-briefcase"></span></a></li>
                        <li><a href="/cart">购物车 <span class="fa fa-shopping-cart"></span></a></li>
                        <li><a href="/auth/logout">退出 {{ Auth::user()->name}}</a></li>
                    @endif
                </ul>
            </div><!-- /.navbar-collapse -->
        </div><!-- /.container-fluid -->
    </nav>
@show

<div class="container">
    @yield('content')
</div>
</body>

</html>

在该文件中我们定义了一个简单的布局:一个导航条以及一些链接。在页面头部加载了 Bootstrap 和 FontAwesome 的 CSS 样式文件,正如你所看到的,我们已经为网站定义了所有需要的菜单项,至于其实现我们将会在下一节讨论,下面我们将会到入之前所说创建商店的后台以便添加商品和文件。

3、后台实现

要保存所有商品信息首先我们需要创建相应的数据表,这里我们创建两个:productsfiles,分别用于商品信息和文件信息。我们使用 Artisan 命令来创建迁移文件:

php artisan make:migration create_table_products
php artisan make:migration create_table_files

编辑相应的迁移文件内容如下:

<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateTableProducts extends Migration
{

    public function up()
    {
        Schema::create('products', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('description');
            $table->float('price');
            $table->string('imageurl');
            $table->string('file_id');
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::drop('products');
    }
}

其中我们使用file_id建立与files表的关联:

<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateTableFiles extends Migration
{

    public function up()
    {
        Schema::create('files', function(Blueprint $table)
        {
            $table->increments('id');
            $table->string('filename');
            $table->string('mime');
            $table->string('original_filename');
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::drop('files');
    }
}

然后我们运行如下命令创建表:

php artisan migrate

数据表创建好了之后我们来创建相应的模型类:

php artisan make:model File
php artisan make:model Product

File模型类保持默认不变,我们在Product模型中新增一个方法建立与File模型的关联:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    public function file()
    {
        return $this->belongsTo('App\File');
    }
}

现在所有模型类以及相关的数据表已经创建好了,接下来我们来编写后台控制器和视图。

商品对应的控制器是ProductController,这里我们暂时先开放所有人可以访问这个控制器,下一节我们将设置只有后台管理员才能访问该控制器,创建控制器的命令如下:

php artisan make:controller ProductController

然后编辑该控制器内容:

<?php

namespace App\Http\Controllers;

use App\Product;

use App\Http\Requests;
use App\Http\Controllers\Controller;
use Request;

use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\File;

class ProductController extends Controller
{

    public function index(){
        $products = Product::all();
        return view('admin.products',['products' => $products]);
    }

    public function destroy($id){
        Product::destroy($id);
        return redirect('/admin/products');
    }

    public function newProduct(){
        return view('admin.new');
    }

    public function add() {

        $file = Request::file('file');
        $extension = $file->getClientOriginalExtension();
        Storage::disk('local')->put($file->getFilename().'.'.$extension,  File::get($file));

        $entry = new \App\File();
        $entry->mime = $file->getClientMimeType();
        $entry->original_filename = $file->getClientOriginalName();
        $entry->filename = $file->getFilename().'.'.$extension;

        $entry->save();

        $product  = new Product();
        $product->file_id=$entry->id;
        $product->name =Request::input('name');
        $product->description =Request::input('description');
        $product->price =Request::input('price');
        $product->imageurl =Request::input('imageurl');

        $product->save();

        return redirect('/admin/products');

    }
}

根据上述代码我们还需要创建两个视图文件,首先是resources/view/admin/new.blade.php

@extends('layouts.master')

@section('新增商品', 'Page Title')

@section('sidebar')
    @parent
@endsection

@section('content')
    <div class="panel panel-info">
        <div class="panel-heading">
            <div class="panel-title">新增商品</div>
        </div>
        <div class="panel-body" >
            <form method="POST" action="/admin/product/save" class="form-horizontal" enctype="multipart/form-data" role="form">
                {!! csrf_field() !!}
                <fieldset>
                    <!-- Text input-->
                    <div class="form-group">
                        <label class="col-md-3 control-label" for="name">名称</label>
                        <div class="col-md-9">
                            <input id="name" name="name" type="text" placeholder="商品名称" class="form-control input-md" required="">

                        </div>
                    </div>
                    <div class="form-group">
                        <label class="col-md-3 control-label" for="textarea">描述</label>
                        <div class="col-md-9">
                            <textarea class="form-control" id="textarea" name="description"></textarea>
                        </div>
                    </div>
                    <div class="form-group">
                        <label class="col-md-3 control-label" for="price">价格</label>
                        <div class="col-md-9">
                            <input id="price" name="price" type="text" placeholder="商品价格" class="form-control input-md" required="">

                        </div>
                    </div>
                    <div class="form-group">
                        <label class="col-md-3 control-label" for="imageurl">图片URL</label>
                        <div class="col-md-9">
                            <input id="imageurl" name="imageurl" type="text" placeholder="商品图片URL" class="form-control input-md" >

                        </div>
                    </div>
                    <div class="form-group">
                        <label class="col-md-3 control-label" for="file">文件</label>
                        <div class="col-md-9">
                            <input id="file" name="file" class="input-file" type="file">
                        </div>
                    </div>
                    <div class="form-group">
                        <label class="col-md-3 control-label" for="submit"></label>
                        <div class="col-md-9">
                            <button id="submit" name="submit" class="btn btn-primary">提交</button>
                        </div>
                    </div>

                </fieldset>

            </form>
        </div>
    </div>
@endsection

然后是resources/view/admin/products.blade.php

@extends('layouts.master')

@section('商店后台', 'Page Title')

@section('sidebar')
    @parent
@endsection

@section('content')

    <div class="container">
        <div class="row">
            <div class="col-md-6">
                <a href="/admin/product/new"><button class="btn btn-success">新增商品</button></a>
            </div>
        </div>
        <div class="row">
            <div class="col-md-12">
                <table class="table table-striped">
                    <thead>
                    <td>名称</td>
                    <td>价格</td>
                    <td>文件</td>
                    <td>操作</td>
                    </thead>
                    <tbody>
                    @foreach ($products as $product)
                        <tr>
                            <td>{{$product->name}}</td>
                            <td>¥{{$product->price}}</td>
                            <td>{{$product->file->original_filename}}</td>
                            <td><a href="/admin/product/destroy/{{$product->id}}"><button class="btn btn-danger">删除</button></a> </td>
                        </tr>
                    @endforeach
                    </tbody>
                </table>
            </div>
        </div>
    </div>


@endsection

最后要做的就是在routes.php中定义路由:

Route::get('/admin/product/new', 'ProductController@newProduct');
Route::get('/admin/products', 'ProductController@index');
Route::get('/admin/product/destroy/{id}', 'ProductController@destroy');
Route::post('/admin/product/save', 'ProductController@add');

这样我们访问后台页面时就能看到如下页面:

Laravel商店后台

添加新商品页面如下:

Laravel 商店新增商品

至此,一个简单的商城后台就这么实现了,对生产环境而言这还不够,但是所有一个商城需要的基本功能已经具备了,你可以添加一个新商品,应用会将其对应的文件存储到指定目录,这里要感谢Laravel提供的Storage API,你可以将文件存在本地,也可以存放到Amazon S3或者Dropbox,只需要修改配置文件就行,不需要修改任何代码。

4、前台展示

后台新增商品后需要在前台将商品展示给用户,我们将相应的逻辑整合到前端控制器MainController中,仍然使用命令行创建这个控制器:

php artisan make:controller MainController

编辑这个控制器的代码如下:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\Product;

class MainController extends Controller
{
    public function index()
    {
        $products = Product::all();
        return view('main.index',['products' => $products]);

    }
}

我们只是简单定义了一个index方法来获取所有商品, 并将其渲染到main.index页面,下面我们来创建这个页面resources/view/main/index.blade.php,编辑文件内容如下:

@extends('layouts.master')

@section('商品列表', 'Page Title')

@section('sidebar')
    @parent
@endsection

@section('content')
    <div class="container">
        <div class="row">
            <div class="col-md-12">
                @foreach ($products as $product)

                    <div class="col-sm-6 col-md-4">
                        <div class="thumbnail" >
                            <img src="{{$product->imageurl}}" class="img-responsive">
                            <div class="caption">
                                <div class="row">
                                    <div class="col-md-6 col-xs-6">
                                        <h3>{{$product->name}}</h3>
                                    </div>
                                    <div class="col-md-6 col-xs-6 price">
                                        <h3>
                                            <label>¥{{$product->price}}</label></h3>
                                    </div>
                                </div>
                                <p>{{$product->description}}</p>
                                <div class="row">
                                    <div class="col-md-6 col-md-offset-3">
                                        <a href="/addProduct/{{$product->id}}" class="btn btn-success btn-product"><span class="fa fa-shopping-cart"></span> 购买</a></div>
                                </div>
                            </div>
                        </div>
                    </div>
                @endforeach
            </div>
        </div>
    </div>

@endsection

和后台一样我们也要定义相应的路由:

Route::get('/', 'MainController@index');

这样我们访问前台页面就能看到创建的商品了:

Laravel 商店商品列表

本章节我们实现了后台添加商品和前台展示商品的功能,下一节我们将实现用户认证和购物车功能。

注:本文整理自https://www.codetutorial.io/how-to-craft-a-digital-e-shop-with-laravel/,完整代码可以从GitHub获取https://github.com/nonfu/laravel-store

学院君

学院君 has written 548 articles

资深PHP工程师,Laravel学院院长