laravel省市区三级联动缓存数据api及地址管理restful api

前言

在构建电商平台或任何需要处理地理位置信息的应用时,准确且高效地管理省市区数据及用户地址信息显得尤为重要。Laravel以其强大的数据库ORM、灵活的路由系统和资源控制器,为开发者提供了构建这类功能的强大支持。
本指南将引导您完成从基础数据构建到复杂功能实现的全过程。首先,我们将学习如何添加省市县数据,创建地址模型以规范数据存储,并通过控制器和路由的设计实现地址信息的基本操作。接下来,我们将深入探讨地址管理的进阶功能,包括地址验证、资源API控制器的配置、以及订单地址的修改等。每一步都将配以详细的实现步骤和测试验证,确保您能够掌握这些关键技能。
通过本指南的学习与实践,您将能够构建一个高效、可维护的地址管理系统,为您的应用提供更加丰富的地理信息服务,增强用户体验并提升应用的价值。

1、省市区

为什么我们这里省市区要写在后台,为什么不直接让前端用前端组件,因为这边的话,我们最后走后台,数据可控制,如果前端一套web组件,一套小程序组件,或者一套app的组件,那么数据格式不一样,就会有各种问题,所以这个数据最好我们给前端。

1.1 添加省市县数据

database文件夹下新建sql文件夹,将省市县数据文件放入:
在这里插入图片描述
运行命令php artisan make:seeder CitySeeder
在这里插入图片描述
CitySeeder.php中写入:

        // 创建省市区表,并填充数据
        DB::unprepared(file_get_contents(__DIR__.'/../sql/provinice_city_county_town.sql'));

在这里插入图片描述
运行填充命令:php artisan db:seed --class=CitySeeder
在这里插入图片描述
在这里插入图片描述
由于城市的数据比较多,所以在一开始填充之后写入辅助函数建立缓存:

    /**
     * 城市相关的缓存信息
     */
    if (!function_exists('city_cache')) {
        function city_cache($pid = 0) {
        // cache()->forget('city_children'.$pid);
            return cache() ->rememberForever('city_children'.$pid, function () use($pid) {
                return City::where('pid', $pid)->get()->keyBy('id');
            });
        }
    }

在这里插入图片描述


1.2 创建地址模型

创建city模型:php artisan make:model City
在这里插入图片描述
设置属性让它去找city表,因为不找的话,它默认去找复数形式的:
protected $table = 'city';
在这里插入图片描述


1.3 创建地址控制器

运行命令php artisan make:controller CityController
在这里插入图片描述
在城市模型中加入关联的子类:

    /**
     * 子类
     */
    public function children() {
        return $this->hasMany(City::class, 'pid', 'id');
    }

在这里插入图片描述
控制器中写入:

    /**
     * 省市县数据
     */
    public function index(Request $request) {
        $resData = city_cache($request->query('pid', 0));
        return $resData;
    }

在这里插入图片描述


1.4 创建地址路由

        /**
         * 地址
         */
        $api->get('city', [CityController::class, 'index']);

在这里插入图片描述

1.5 测试效果

不传pid时获取到所有的省:
在这里插入图片描述
前端用户选中其中一个省,把id传进去获得市的数据:
在这里插入图片描述
将点击市,获取市下的数据:
在这里插入图片描述


2、地址管理

2.1 创建地址模型以及迁移文件

运行命令php artisan make:model Address -m
在这里插入图片描述
在迁移文件中创建表结构:

Schema::create('addresses', function (Blueprint $table) {
            $table->id();
            $table->integer('user_id')->comment('用户id');
            $table->string('name')->comment('收获人');
            $table->integer('city_id')->comment('city表中的id');
            $table->string('address')->comment('详细地址');
            $table->string('phone')->comment('手机号');
            $table->tinyInteger('is_default')->default(0)->comment('默认地址 0=>不是默认,1=>是默认');
            $table->timestamps();
            $table->index('user_id'); // 查询索引
        });

在这里插入图片描述
运行命令php artisan migrate
在这里插入图片描述


2.2 创建地址验证类

运行命令php artisan make:request Web/AddressRequest
在这里插入图片描述
写入:

    public function rules()
    {
        return [
            'name' => 'required',
            'city_id' => [
                'required',
                function ($attribute, $value, $fail) {
                    $city = City::find($value);
                    if (empty($city)) $fail('城市不存在');
                },
            ],
            'address' => 'required',
            'phone' => 'required|regex:/^1[3-9]\d{9}$/',
        ];
    }

    /**
     * 提示消息
     */
    public function messages() {
        return [
            'name.required' => '收货人必填',
            'city_id.required' => '地址不能为空',
            'address.required' => '详细地址不能为空',
            'phone.required' => '手机号不能为空',
        ];
    }

在这里插入图片描述


2.3 配置地址transform创建地址资源API控制器

给模型配置可批量插入的字段:

    use HasFactory;
    protected $fillable = ['user_id','name', 'phone', 'address', 'city_id', 'is_default'];

在这里插入图片描述


City.php模型中写入查找父级的联动:

    /**
     * 查找父类
     */
    public function parent() {
        return $this->belongsTo(City::class, 'pid', 'id');
    }

在这里插入图片描述

在辅助函数中写入根据区id查找上级联动数据:

    /**
     * 通过区ID 查询完整省市区数据
     */
    if (!function_exists('city_name')) {
        function city_name($city_id) {
            $city = City::where('id', $city_id)->with('parent.parent.parent')->first();
            // $str = $city['parent']['parent']['parent']['name'] ?? '';
            // $str .= $city['parent']['parent']['name'] ?? '';
            // $str .= $city['parent']['name'] ?? '';
            // $str .= $city['name'] ?? '';
            $str = [ 
                $city['parent']['parent']['parent']['name'] ?? '',
                $city['parent']['parent']['name'] ?? '',
                $city['parent']['name'] ?? '',
                $city['name'] ?? ''
            ];

            return trim(implode(' ', $str));
        }
    }

在这里插入图片描述

创建AddressTransformer.php写入:

<?php 

namespace App\Transformers;

use App\Models\Address;
use League\Fractal\TransformerAbstract;

class AddressTransformer extends TransformerAbstract {
    public function transform(Address $address) {
        return [ 
            'id' => $address->id,
            'name' => $address->name,
            'city_id' => $address->city_id,
            'city_name' => city_name($address->city_id),
            'phone' => $address->phone,
            'address' => $address->address,
            'is_default' => $address->is_default,
            'created_at' => $address->created_at,
            'updated_at' => $address->updated_at,
        ];
    }
}
 

在这里插入图片描述

运行命令php artisan make:controller Web/AddressController --api
在这里插入图片描述
写入增删改查以及是否默认方法:

<?php

namespace App\Http\Controllers\Web;

use App\Http\Controllers\BaseController;
use App\Http\Requests\Web\AddressRequest;
use App\Models\Address;
use App\Transformers\AddressTransformer;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class AddressController extends BaseController
{
    /**
     * 我的地址列表
     */
    public function index()
    {
        $address = Address::where('user_id', auth('api')->id())->get();
        return $this->response->collection($address, new AddressTransformer());
    }

    /**
     * 添加地址
     */
    public function store(AddressRequest $request)
    {
        Address::create([
            'user_id' => auth('api')->id(),
            'name' => $request->input('name'),
            'phone' => $request->input('phone'),
            'address' => $request->input('address'),
            'city_id' => $request->input('city_id'),
        ]);
        return $this->response->created();
    }

    /**
     * 地址详情
     */
    public function show(Address $address)
    {
        return $this->response->item($address, new AddressTransformer());
    }

    /**
     * 更新地址
     */
    public function update(AddressRequest $request, Address $address)
    {
        $address->update([
            'user_id' => auth('api')->id(),
            'name' => $request->input('name'),
            'phone' => $request->input('phone'),
            'address' => $request->input('address'),
            'city_id' => $request->input('city_id'),
            'is_default' => $request->input('is_default')
        ]);
        return $this->response->noContent();
    }

    /**
     * 删除地址
     */
    public function destroy(Address $address)
    {
        $address->delete();
        return $this->response->noContent();
    }

    /**
     * 默认地址
     */
    public function isDefault(Address $address)
    {
        if ($address->is_default == 1) { 
            return $this->response->noContent(); // 如果改地址已经设置默认地址 直接返回
        }
        try {
            DB::beginTransaction(); // 开启事物, 防止一个设置成功一个设置失败
            // 先把所有的地址都设置为非默认
            $default_address = Address::where('user_id', auth('api')->id())
                ->where('is_default', 1)
                ->first();
            if (!empty($default_address)) {
                
                $default_address->is_default = 0;
                $default_address->save();
            }
            // 在把当前的设置成默认
            $address->is_default = 1;
            $address->save();
            DB::commit();
            return $this->response->noContent();
        } catch (\Exception $e) {
            DB::rollback();
            throw $e;
        }
    }
}


2.4 配置地址路由

 // 地址增删改查
        $api->resource('address', AddressController::class);
        // 设置默认地址
        $api->patch('address/{address}/default', [AddressController::class, 'isDefault']);

在这里插入图片描述


2.5 订单地址修改

由于之前没有写地址相关的api,地址那块是模拟的假数据,现在把它修改过来:

// 地址数据
        $address = Address::where('user_id', auth('api')->id())
                    ->orderBy('is_default', 'desc')
                    ->get();

在这里插入图片描述


还有一处地方就是在提交订单时候验证地址是否存在:

'address_id' => 'required|exixts:addresses,id'

在这里插入图片描述


2.6 测试效果

1、添加地址
在这里插入图片描述

2、地址列表

在这里插入图片描述

3、修改地址
在这里插入图片描述

4、地址详情
在这里插入图片描述
5、删除地址
在这里插入图片描述
6、设置默认地址
在这里插入图片描述

在学习的php的路上,如果你觉得本文对你有所帮助的话,那就请关注点赞评论三连吧,谢谢,你的肯定是我写博的另一个支持。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你华还是你华

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值