laravel 商城实战开发
项目初始化
数据库
UserName: homestead
Password: secret
数据字典
-
用户表
-
商品表 -> 用户
-
订单表 -> 用户 商品
-
评价表 -> 用户 商品 订单表
-
轮播图 ->
-
分类表 -> 分类表
-
购物车表 -> 用户 商品
-
用户地址表 -> 用户
-
用户收藏表 -> 用户 商品
创建模型
php artisan make:model Address
php artisan make:model Cart
php artisan make:model Category
php artisan make:model City
php artisan make:model Collect
php artisan make:model Comment
php artisan make:model Good
php artisan make:model Order
php artisan make:model OrderDetails
php artisan make:model Slide
购物车模型
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Cart extends Model
{
use HasFactory;
// 允许批量赋值的字段
protected $fillable = ['user_id', 'goods_id', 'num'];
/**
* 所关联的商品
*/
public function goods()
{
return $this->belongsTo(Good::class, 'goods_id', 'id');
}
}
分类模型
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
use HasFactory;
// 可批量赋值的字段
protected $fillable = ['name', 'pid', 'level', 'group'];
/**
* 分类的子类
*/
public function children()
{
return $this->hasMany(Category::class, 'pid', 'id');
}
}
城市表模型
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class City extends Model
{
use HasFactory;
// 指定模型关联的表名
protected $table = 'city';
/**
* 子类
*/
public function children()
{
return $this->hasMany(City::class, 'pid', 'id');
}
/**
* 父级
*/
public function parent()
{
return $this->belongsTo(City::class, 'pid', 'id');
}
}
收藏表模型
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Collect extends Model
{
use HasFactory;
protected $guarded = [];
/**
* 收藏所属于的商品, 一对一
*/
public function goods()
{
return $this->belongsTo(Good::class, 'goods_id', 'id');
}
}
评价表模型
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
use HasFactory;
// 不允许批量赋值的字段
protected $guarded = [];
/**
* 强制转换的属性
*
* @var array
*/
protected $casts = [
'pics' => 'array',
];
/**
* 评论所属用户
*/
public function user()
{
return $this->belongsTo(User::class, 'user_id', 'id');
}
/**
* 评论所属商品
*/
public function goods()
{
return $this->belongsTo(Good::class, 'goods_id', 'id');
}
}
商品表模型
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Good extends Model
{
use HasFactory;
// 可批量赋值的字段
protected $fillable = [
'title',
'user_id',
'category_id',
'description',
'price',
'stock',
'cover',
'pics',
'is_on',
'is_recommend',
'details'
];
/**
* 强制转换的属性
*
* @var array
*/
protected $casts = [
'pics' => 'array',
];
/**
* 商品所属的分类
*/
public function category()
{
return $this->belongsTo(Category::class, 'category_id', 'id');
}
/**
* 商品所属的用户
*/
public function user()
{
return $this->belongsTo(User::class, 'user_id', 'id');
}
/**
* 商品所有的评价
*/
public function comments()
{
return $this->hasMany(Comment::class, 'goods_id', 'id');
}
/**
* 商品所有的收藏
*/
public function collects()
{
return $this->hasMany(Collect::class, 'goods_id', 'id');
}
}
订单表模型
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Order extends Model
{
use HasFactory;
// 可以批量赋值的字段
protected $fillable = [
'user_id',
'order_no',
'amount',
'address_id',
'status',
'trade_no',
'pay_type',
'pay_time'
];
/**
* 所属用户
*/
public function user()
{
return $this->belongsTo(User::class, 'user_id', 'id');
}
/**
* 订单拥有的订单细节
*/
public function orderDetails()
{
return $this->hasMany(OrderDetails::class, 'order_id', 'id');
}
/**
* 订单所关联的地址
*/
public function orderAddress()
{
return $this->hasOne(Address::class, 'id', 'address_id');
}
/**
* 订单远程一对多, 关联的商品
*/
public function goods()
{
return $this->hasManyThrough(
Good::class, // 最终关联的模型
OrderDetails::class, // 中间模型
'order_id', // 中间模型和本模型关联的外键
'id', // 最终关联模型的外键
'id', // 本模型和中间模型关联的键
'goods_id' // 中间表和最终模型关联的一个键
);
}
}
订单详情表模型
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class OrderDetails extends Model
{
use HasFactory;
// 可批量赋值的字段
protected $fillable = ['order_id', 'goods_id', 'price', 'num'];
/**
* 细节所属订单主表
*/
public function order()
{
return $this->belongsTo(Order::class, 'order_id', 'id');
}
/**
* 细节所关系的商品
*/
public function goods()
{
return $this->hasOne(Good::class, 'id', 'goods_id');
}
}
轮播图表模型
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Slide extends Model
{
use HasFactory;
protected $fillable = ['title', 'url', 'img', 'status', 'seq'];
}
语言包
composer require laravel-lang/lang:~8.0
复制文件
使用composer(如上所述)将依赖项添加到您的应用程序后,您可以在目录下找到语言文件vendor/laravel-lang/lang
。
vendor/laravel-lang/lang/src/zh_CN 复制到 resources/lang/zh_CN
config/app.php
'locale' => 'zh_CN',
时区
config/app.php
'timezone' => 'Asia/Shanghai',
或者修改为:PRC
Dingo API
安装
composer require dingo/api
Laravel
如果您想在配置文件中进行配置更改,您可以使用以下 Artisan 命令发布它(否则,不需要此步骤):
git init
git status
git add .
git commit -m '初始化项目'
php artisan vendor:publish --provider="Dingo\Api\Provider\LaravelServiceProvider"
配置相应
<?php
namespace App\Http\Controllers;
use Dingo\Api\Routing\Helpers;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
use Helpers;
}
配置信息
API_STANDARDS_TREE=x
API_SUBTYPE=shop
API_PREFIX=api
API_VERSION=v1
API_NAME=shop
API_CONDITIONAL_REQUEST=false
API_STRICT=false
API_DEFAULT_FORMAT=json
API_DEBUG=true
创建路由文件
App\Providers\RouteServiceProvider
// 用户认证相关路由
Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/auth.php'));
// 前台路由
Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
// 后台路由
Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/admin.php'));
路由的使用
版本组
为了避免与你主要的项目路由冲突,dingo/api 将会使用其专属的路由实例。要创建端点,我们首先需要获得一个 API 路由的实例:
$api = app('Dingo\Api\Routing\Router');
现在我们必须定义一个版本分组。这种定义方式有利于后续为相同端点新增多版本支持。
$api->version('v1', function ($api) {
});
如果你想一个分组返回多个版本,只需要传递一个版本数组。
$api->version(['v1', 'v2'], function ($api) {
});
通过在第二个参数上传递一个属性数组,你也可以将此组视为特定框架的标准组。
$api->version('v1', ['middleware' => 'foo'], function ($api) {
});
你还可以嵌套常规组以进一步定制某些端点。
$api->version('v1', function ($api) {
$api->group(['middleware' => 'foo'], function ($api) {
});
});
创建路由
一旦你有了一个版本分组,你就可以在分组闭包的参数中,通过 $api
创建端点。
$api->version('v1', function ($api) {
$api->get('users/{id}', 'App\Api\Controllers\UserController@show');
});
创建前台控制器
php artisan make:controller Api/UserController
响应
直接返回模型
class UserController
{
public function show()
{
return User::all();
}
}
你可以返回一个单一的用户。
class UserController
{
public function show($id)
{
return User::findOrFail($id);
}
}
响应生成器
响应生成器提供了一个流畅的接口去方便的建立一个更定制化的响应。响应的生成器通常是与 transformer 相结合。
要利用响应生成器,你的控制器需要使用 Dingo\Api\Routing\Helpers
trait。为了在你的控制器里保持引入和使用这个 trait,你可以创建一个基础控制器,然后你的所有的 API 控制器都继承它。
use Dingo\Api\Routing\Helpers;
use Illuminate\Routing\Controller;
class BaseController extends Controller
{
use Helpers;
}
现在你的控制器可以直接继承基础控制器。响应生成器可以在控制器里通过 $response
属性获取。
响应一个数组
class UserController extends BaseController
{
public function show($id)
{
$user = User::findOrFail($id);
return $this->response->array($user->toArray());
}
}
响应一个元素
class UserController extends BaseController
{
public function show($id)
{
$user = User::findOrFail($id);
return $this->response->item($user, new UserTransformer);
}
}
每个Transformer 可以对应一个模型,用来格式化响应的数据。Transformers
创建在APP目录下。Transformers 允许你便捷地、始终如一地将对象转换为一个数组。通过使用一个 transformer 你可以对整数和布尔值,包括分页结果和嵌套关系进行类型转换。
<?php
namespace App\Transformers;
use App\Models\User;
use League\Fractal\TransformerAbstract;
class UserTransformer extends TransformerAbstract
{
public function transform(User $user)
{
return [
'id' => $user->id,
'name' => $user->name,
'email' => $user->email,
'phone' => $user->phohe,
'avatar' => $user->avatar,
'openid' => $user->openid,
];
}
}
响应一个元素集合
class UserController extends BaseController
{
public function index()
{
$users = User::all();
return $this->response->collection($users, new UserTransformer);
}
}
分页响应
class UserController extends BaseController
{
public function index()
{
$users = User::paginate(25);
return $this->response->paginator($users, new UserTransformer);
}
}
无内容响应
return $this->response->noContent();
创建了资源的响应
return $this->response->created();
错误响应
这有很多不同的方式创建错误响应,你可以快速的生成一个错误响应。
// 一个自定义消息和状态码的普通错误。
return $this->response->error('This is an error.', 404);
// 一个没有找到资源的错误,第一个参数可以传递自定义消息。
return $this->response->errorNotFound();
// 一个 bad request 错误,第一个参数可以传递自定义消息。
return $this->response->errorBadRequest();
// 一个服务器拒绝错误,第一个参数可以传递自定义消息。
return $this->response->errorForbidden();
// 一个内部错误,第一个参数可以传递自定义消息。
return $this->response->errorInternal();
// 一个未认证错误,第一个参数可以传递自定义消息。
return $this->response->errorUnauthorized();
添加 Meta 信息
return $this->response->item($user, new UserTransformer)->addMeta('foo', 'bar');
API 节流
节流限速 (throttling) 允许你限制客户端给定时间的访问次数。限制和过期时间是在限速器里定义的。 默认有两个限速器,验证通过限速器和未验证限速器。
启用节流限制
要为路由或路由组启用节流限制,你必须启用 api.throttle
中间件。 一旦启用了节流限制,你必须已经配置过了一些限制或配置过了具体的路由限制。
在所有的路由中启用节流限制
$api->version('v1', ['middleware' => 'api.throttle'], function ($api) {
// 此版本组中的路由将需要身份认证.
});
路由特定节流
如果只是想限制某些路由或者路由群组,可使用 limit
和 expires
选项。
$api->version('v1', function ($api) {
$api->get('users', ['middleware' => 'api.throttle', 'limit' => 100, 'expires' => 5, function () {
return User::all();
}]);
});
以上为这个路由设置了请求限制 100 次,过期时间 5 分钟。如果你把它设置在路由群组上,那组内的每个路由具有 100 次请求的限制。
$api->version('v1', ['middleware' => 'api.throttle', 'limit' => 100, 'expires' => 5], function ($api) {
$api->get('users', function () {
return User::all();
});
$api->get('posts', function () {
return Post::all();
});
});