使用 Laravel 纯手工打造一个简单的电子商务网站(二) —— 用户认证及购物车功能实现
1、概述
上一节我们演示了如何在后台添加商品以及在前台显示,这一节我们将为商店实现用户认证和购物车功能。购物车需要和用户关联并存储到数据库,以便用户下次登录还能找到自己的购物车。
因此我们首先要实现用户注册登录功能,这一实现在用户认证文档中已有详细说明,这里我们会一带而过。
注:关于本教程的完整代码已公开到GitHub:https://github.com/nonfu/laravel-store
2、用户认证
Laravel 5.2 为我们提供了认证脚手架,只需要简单运行如下命令就可以实现用户认证需要的所有代码,这里我们自己来手工打造这一功能。
首先在resources/views
目录下创建一个auth
子目录,在这个子目录中创建注册页面register.blade.php
和login.blade.php
,首先编辑login.blade.php
内容如下:
@extends('layouts.master')
@section('用户登录', 'Page Title')
@section('sidebar')
@parent
@endsection
@section('content')
<div class="container">
<div id="loginbox" style="margin-top:50px;" class="col-md-6 col-md-offset-3 col-sm-8 col-sm-offset-2">
<div class="panel panel-info" >
<div class="panel-heading">
<div class="panel-title">用户登录</div>
</div>
<div style="padding-top:30px" class="panel-body" >
<div style="display:none" id="login-alert" class="alert alert-danger col-sm-12"></div>
<form method="POST" action="/auth/login" class="form-horizontal" role="form">
{!! csrf_field() !!}
<div style="margin-bottom: 25px" class="input-group">
<span class="input-group-addon"><i class="fa fa-user"></i></span>
<input id="email" type="text" class="form-control" name="email" value="" placeholder="邮箱">
</div>
<div style="margin-bottom: 25px" class="input-group">
<span class="input-group-addon"><i class="fa fa-lock"></i></span>
<input id="password" type="password" class="form-control" name="password" placeholder="密码">
</div>
<div class="input-group">
<div class="checkbox">
<label>
<input iremember" type="checkbox" name="remember" value="1"> 记住我
</label>
</div>
</div>
<div style="margin-top:10px" class="form-group">
<div class="col-sm-12 controls">
<button type="submit" id="btn-login" href="#" class="btn btn-success">登录 </button>
</div>
</div>
<div class="form-group">
<div class="col-md-12 control">
<div style="border-top: 1px solid#888; padding-top:15px; font-size:85%" >
<a href="/auth/register" >
没有帐号?点此注册
</a>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
@endsection
然后编辑register.blade.php
内容如下:
@extends('layouts.master')
@section('用户注册', 'Page Title')
@section('sidebar')
@parent
@endsection
@section('content')
<div style="margin-top:50px" class="mainbox col-md-6 col-md-offset-3 col-sm-8 col-sm-offset-2">
<div class="panel panel-info">
<div class="panel-heading">
<div class="panel-title">用户注册</div>
<div style="float:right; font-size: 85%; position: relative; top:-10px"><a id="signinlink" href="/auth/login">已有帐号?前往注册>></a></div>
</div>
<div class="panel-body" >
<form method="POST" action="/auth/register" class="form-horizontal" role="form">
{!! csrf_field() !!}
<div class="form-group">
<label for="email" class="col-md-3 control-label">邮箱</label>
<div class="col-md-9">
<input type="text" class="form-control" name="email" placeholder="邮箱地址">
</div>
</div>
<div class="form-group">
<label for="firstname" class="col-md-3 control-label">用户名</label>
<div class="col-md-9">
<input type="text" class="form-control" name="name" placeholder="用户名">
</div>
</div>
<div class="form-group">
<label for="password" class="col-md-3 control-label">密码</label>
<div class="col-md-9">
<input type="password" class="form-control" name="password" placeholder="密码">
</div>
</div>
<div class="form-group">
<label for="password" class="col-md-3 control-label">确认密码</label>
<div class="col-md-9">
<input type="password" class="form-control" name="password_confirmation" placeholder="确认密码">
</div>
</div>
<div class="form-group">
<!-- Button -->
<div class="col-md-offset-3 col-md-9">
<button type="submit" id="btn-signup" class="btn btn-success"><i class="fa fa-hand-o-right"></i> 注册</button>
</div>
</div>
</form>
</div>
</div>
</div>
@endsection
注册和登录页面都完成后我们来定义相应路由:
// 认证路由... Route::get('auth/login', 'Auth\AuthController@getLogin'); Route::post('auth/login', 'Auth\AuthController@postLogin'); Route::get('auth/logout', 'Auth\AuthController@getLogout'); // 注册路由... Route::get('auth/register', 'Auth\AuthController@getRegister'); Route::post('auth/register', 'Auth\AuthController@postRegister');
这样用户就可以通过相应路由实现注册、登录及退出了。注册和登录的链接已经在页面顶部导航中列出了,点击“注册”进入登录页面:
注册成功后会自动登录并跳转到首页:
3、购物车实现
实现购物车之前首先要创建对应的数据表,这里我们需要两张表:carts
和cart_items
,分别用于存放购物车信息和购物车商品明细。还是使用Artistan命令生成迁移文件:
php artisan make:migration create_table_carts php artisan make:migration create_table_carts_item
编辑迁移文件如下:
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateTableCarts extends Migration
{
public function up()
{
Schema::create('carts', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id');
$table->timestamps();
});
}
public function down()
{
Schema::drop('carts');
}
}
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateTableCartItems extends Migration
{
public function up()
{
Schema::create('cart_items', function (Blueprint $table) {
$table->increments('id');
$table->integer('cart_id');
$table->integer('product_id');
$table->timestamps();
});
}
public function down()
{
Schema::drop('cart_items');
}
}
运行如下命令生成对应数据表:
php artisan migrate
数据表创建好了之后需要创建对应的模型类:
php artisan make:model Cart php artisan make:model CartItem
编辑Cart
模型类如下:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Cart extends Model
{
public function user()
{
return $this->belongsTo('App\User');
}
public function cartItems()
{
return $this->hasMany('App\CartItem');
}
}
编辑CartItem
模型类如下:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class CartItem extends Model
{
public function cart()
{
return $this->belongsTo('App\Cart');
}
public function product()
{
return $this->belongsTo('App\Product');
}
}
具体功能实现定义在CartController
中,使用Artisan命令创建这个控制器:
php artisan make:controller CartController
编辑这个控制器的内容如下:
<?php
namespace App\Http\Controllers;
use App\Cart;
use App\CartItem;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
class CartController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function addItem ($productId){
$cart = Cart::where('user_id',Auth::user()->id)->first();
if(!$cart){
$cart = new Cart();
$cart->user_id=Auth::user()->id;
$cart->save();
}
$cartItem = new Cartitem();
$cartItem->product_id=$productId;
$cartItem->cart_id= $cart->id;
$cartItem->save();
return redirect('/cart');
}
public function showCart(){
$cart = Cart::where('user_id',Auth::user()->id)->first();
if(!$cart){
$cart = new Cart();
$cart->user_id=Auth::user()->id;
$cart->save();
}
$items = $cart->cartItems;
$total=0;
foreach($items as $item){
$total+=$item->product->price;
}
return view('cart.view',['items'=>$items,'total'=>$total]);
}
public function removeItem($id){
CartItem::destroy($id);
return redirect('/cart');
}
}
我们来简单看看这个控制器的代码,我们在构造函数__construct
中使用了认证中间件,表示用户需要登录后才能访问这个控制器的所有动作,比如添加商品到购物车,查看购物车等。
第二个方法是addItem
,这个方法首先查询与当前登录用户关联的购物车,如果获取数据为空,表示用户购物车为空,这就需要我们创建一个新的购物车实例并保存到数据库,有了Cart
实例后就可以将新的CartItem
关联到这个购物车,添加完成后重定向到购物车页面/cart
。
/cart
路由会被showCart
方法解析,这个方法和addProduct
类似,只不过是从数据库获取与用户关联的Cart
,如果没有的话会去创建一个新的实例,然后循环遍历购物车明细并计算总价,最终将数据$items
和$total
渲染到页面视图。
最后一个方法是removeItem
,该方法会删除数据库中与当前Cart
关联的对应CartItem
。
下面我们来创建页面视图,只需要一个足矣,在resources/views/cart
目录下创建view.blade.php
,编辑页面内容如下:
@extends('layouts.master')
@section('购物车', 'Page Title')
@section('sidebar')
@parent
@endsection
@section('content')
<div class="row">
<div class="col-sm-12 col-md-10 col-md-offset-1">
<table class="table table-hover">
<thead>
<tr>
<th>商品</th>
<th></th>
<th class="text-center"></th>
<th class="text-center">小计</th>
<th> </th>
</tr>
</thead>
<tbody>
@foreach($items as $item)
<tr>
<td class="col-sm-8 col-md-6">
<div class="media">
<a class="thumbnail pull-left" href="#"> <img class="media-object" src="{{$item->product->imageurl}}" style="width: 100px; height: 72px;"> </a>
<div class="media-body">
<h4 class="media-heading"><a href="#">{{$item->product->name}}</a></h4>
</div>
</div></td>
<td class="col-sm-1 col-md-1" style="text-align: center">
</td>
<td class="col-sm-1 col-md-1 text-center"></td>
<td class="col-sm-1 col-md-1 text-center"><strong>${{$item->product->price}}</strong></td>
<td class="col-sm-1 col-md-1">
<a href="/removeItem/{{$item->id}}"> <button type="button" class="btn btn-danger">
<span class="fa fa-remove"></span> 移除
</button>
</a>
</td>
</tr>
@endforeach
<tr>
<td> </td>
<td> </td>
<td> </td>
<td><h3>总价</h3></td>
<td class="text-right"><h3><strong>${{$total}}</strong></h3></td>
</tr>
<tr>
<td> </td>
<td> </td>
<td> </td>
<td>
<a href="/"> <button type="button" class="btn btn-default">
<span class="fa fa-shopping-cart"></span> 继续购物
</button>
</a></td>
<td>
<button type="button" class="btn btn-success">
结算 <span class="fa fa-play"></span>
</button></td>
</tr>
</tbody>
</table>
</div>
</div>
@endsection
之后定义路由规则如下:
Route::get('/addProduct/{productId}', 'CartController@addItem'); Route::get('/removeItem/{productId}', 'CartController@removeItem'); Route::get('/cart', 'CartController@showCart');
这样用户登录后,添加商品到购物车,在浏览器中访问购物车页面如下:
至此,我们的商城基本上完成了,只剩最后一步——下单支付,将购物车转化为订单并获取下载链接,关于这一实现我们将在下一节讨论。
21 Comments