Laravel代码简洁之道和性能优化

简介: 这个 Laravel 扩展为查询构建器和 Eloquent 添加了对 INSERT & UPDATE (UPSERT) 和 INSERT IGNORE 的支持

思考:如何提高Model层查询DB的效率?如何精简代码?


  • 经过一番调研之后发现了一个堪称神器的扩展:laravel-upsert
  • 这个 Laravel 扩展为查询构建器和 Eloquent 添加了对 INSERT & UPDATE (UPSERT) 和 INSERT IGNORE 的支持


先简单说明一下业务场景:


  1. 首先表结构设计是:互相喜欢和添加联系人都是双向关系,即入库A B,B A这样成对的双向数据
  2. 触发互相喜欢,插入2条双向数据,插入之前校验是否存在,存在不重复添加
  3. 如果互相喜欢,则添加双向联系人关系,插入之前校验是否存在,存在则更新type等字段,不存在则插入双向数据


我们通过这个场景能非常好的体会laravel-upsert的强大,不仅减少了代码量,也减少了sql 查询次数,提升了性能。


优化代码前


//校验是否存储
    public static function checkExist($userid, $otherUserid)
    {
        return self::query()
            ->where('userid', $userid)
            ->where('otherUserid', $otherUserid)
            ->exists();
    }
    //添加双向好友关系
    public static function addBoth($userid, $otherUserid)
    {
        if (!self::checkExist($userid, $otherUserid)) {
            UserRelationBoth::insert([
                [
                    'userid' => $userid,
                    'otherUserid' => $otherUserid,
                    'createtime' => time(),
                    'updatetime' => time(),
                ],
                [
                    'userid' => $otherUserid,
                    'otherUserid' => $userid,
                    'createtime' => time(),
                    'updatetime' => time(),
                ]
            ]);
            //互相喜欢,添加好友关系
            AppointmentContacts::saveContacts($userid, $otherUserid, AppointmentContacts::TYPE_RELATION_LIKE_EACHOTHER);
        }
    }
        //保存双向联系人
    public static function saveContacts($userid, $otherUserid, $type, $appointmentPrepareId = 0)
    {
        if (!self::checkExist($userid, $otherUserid)) {
            AppointmentContacts::insert([
                [
                    'userid' => $userid,
                    'otherUserid' => $otherUserid,
                    'appointmentPrepareId' => 0,
                    'type' => $type,
                    'createtime' => time(),
                    'updatetime' => time(),
                ],
                [
                    'userid' => $otherUserid,
                    'otherUserid' => $userid,
                    'appointmentPrepareId' => 0,
                    'type' => $type,
                    'createtime' => time(),
                    'updatetime' => time(),
                ]
            ]);
        } else {
            //存在更新状态
            self::updateContactsType($userid, $otherUserid, $type, $appointmentPrepareId);
        }
    }
     //更新最新关系类型
    public static function updateContactsType($userid, $otherUserid, $type, $appointmentPrepareId = 0)
    {
        self::query()
            ->where('userid', $userid)
            ->where('otherUserid', $otherUserid)
            ->update([
                'type' => $type,
                'appointmentPrepareId' => $appointmentPrepareId,
                'updatetime' => time(),
            ]);
        self::query()
            ->where('userid', $otherUserid)
            ->where('otherUserid', $userid)
            ->update([
                'type' => $type,
                'appointmentPrepareId' => $appointmentPrepareId,
                'updatetime' => time(),
            ]);
    }


代码优化后


public static function addBoth($userid, $otherUserid)
    {
        self::query()->insertIgnore([
            ["userid" => $userid, "otherUserid" => $otherUserid],
            ["userid" => $otherUserid, "otherUserid" => $userid]
        ]);
        //互相喜欢,添加好友关系
        AppointmentContacts::saveContacts($userid, $otherUserid, AppointmentContacts::TYPE_RELATION_LIKE_EACHOTHER);
    }
        //保存双向联系人
    public static function saveContacts($userid, $otherUserid, $type, $appointmentPrepareId = 0)
    {
        //没有添加 有则更新
        self::upsert([
            ["userid" => $userid, "otherUserid" => $otherUserid, 'type' => $type, "appointmentPrepareId" => $appointmentPrepareId],
            ["userid" => $otherUserid, "otherUserid" => $userid, 'type' => $type, "appointmentPrepareId" => $appointmentPrepareId]
        ],
            ['userid', 'otherUserid'],
            ['type' => $type, "appointmentPrepareId" => $appointmentPrepareId]
        );
    }


优化效果


代码量:优化前82行代码,优化后22行代码,代码行数少了3倍+

查询sql的条数:优化前5条sql,优化后2条sql


laravel-upsert 扩展的特性


安装


composer require staudenmeir/laravel-upsert:"^1.0"


用法


插入和更新 (UPSERT)


考虑这个users具有唯一username列的表:


Schema :: create ( 'users' , function ( Blueprint  $ table ) {
     $ table -> increments ( 'id' );
     $ table -> string ( 'username' )-> unique ();
     $ table -> boolean ( ' active' );
     $ table ->时间戳();
});


使用upsert()插入一个新的用户或更新现有的一个。在此示例中,将重新激活非活动用户并updated_at更新时间戳:


DB :: table ( 'users' )-> upsert (
    [ 'username' => 'foo' , 'active' => true , 'created_at' => now (), 'updated_at' => now ()],
     'username' ,
    [ '活动','updated_at' ]
);


提供要作为第一个参数插入的值。这可以是单个记录或多个记录。


第二个参数是唯一标识记录的列。除 SQL Server 外的所有数据库都要求这些列具有PRIMARY或UNIQUE索引。


提供要更新的列作为第三个参数(可选)。默认情况下,将更新所有列。您可以提供带有文字或原始表达式的列名和键值对(见下文)。


作为使用复合键和原始表达式的示例,请考虑以下表,该表计算每个帖子和每天的访问者:


Schema :: create ( 'stats' , function ( Blueprint  $ table ) {
     $ table -> unsignedInteger ( 'post_id' );
     $ table -> date ( 'date' );
     $ table -> unsigned Integer ( 'views' );
     $表->主要([ 'post_id','date' ]);
});


使用upsert()登录访问。该查询将为每个帖子和日期创建一个新记录或增加现有的查看计数器:


DB :: table ( 'stats' )-> upsert (
    [
        [ 'post_id' => 1 , 'date' => now ()-> toDateString (), 'views' => 1 ],
        [ 'post_id' => 2 , 'date' => now ()-> toDateString (), 'views' => 1 ],
    ],
    [ 'post_id','日期' ],
    [ 'views' => DB :: raw ( 'stats.views + 1' )]
);


插入忽略


您还可以在忽略重复键错误的同时插入记录:


Schema :: create ( 'users' , function ( Blueprint  $ table ) {
     $ table -> increments ( 'id' );
     $ table -> string ( 'username' )-> unique ();
     $ table -> timestamps () ;
});
DB :: table ( 'users' )-> insertIgnore ([
    [ 'username' => 'foo' , 'created_at' => now (), 'updated_at' => now ()],
    [ 'username' => 'bar' , 'created_at' => now (), 'updated_at' => now ()],
]);


SQL Server 需要带有唯一标识记录的列的第二个参数:


DB :: table ( 'users' )-> insertIgnore (
    [ 'username' => 'foo' , 'created_at' => now (), 'updated_at' => now ()],
     'username' 
);


Eloquent


你可以在 Eloquent 模型中使用 UPSERT 和 INSERT IGNORE 查询。


在 Laravel 5.5-5.7 中,这需要HasUpsertQueriestrait:


class User extends Model
{
    use \Staudenmeir\LaravelUpsert\Eloquent\HasUpsertQueries;
}
User::upsert(['username' => 'foo', 'active' => true], 'username', ['active']);
User::insertIgnore(['username' => 'foo']);


如果模型使用时间戳,upsert()并且insertIgnore()会自动为插入的值添加时间戳。upsert()还将添加updated_at到更新的列中。


Lumen


如果您使用 Lumen,则必须手动实例化查询构建器:


$builder = new \Staudenmeir\LaravelUpsert\Query\Builder(app('db')->connection());
$builder->from(...)->upsert(...);


在 Eloquent 中,所有版本的 LumenHasUpsertQueries都需要该特性。


注意的问题


  • 要根据需求添加唯一索引
  • 根据官方文档中的说明,我们的model中必须添加这行代码,才能以Eloquent的方式用


use \Staudenmeir\LaravelUpsert\Eloquent\HasUpsertQueries;


  • 因为我们数据库的时间是int类型,不是laravel默认的时间格式,并且我们的插入时间和更新时间也不是laravel默认的字段,我们需要做如下定义:


//时间戳类型
    public $timestamps = true;
    //重写插入和修改时间的字段名
    const CREATED_AT = 'createtime';
    const UPDATED_AT = 'updatetime';
    //设置日期格式为时间戳
    protected $dateFormat = 'U';
    //如果取值有使用toArray()转成数组的话还需要下方的配置
    //获得创建时间
    protected function getCreatetimeAttribute($value)
    {
        return intval($value);
    }
    //获得修改时间
    protected function getUpdatetimeAttribute($value)
    {
        return intval($value);
    }


总结


  • 代码优化是一个不断优化的过程,好久没有更新文章啦,最近会更新一波代码简洁之道和性能优化的文章,包括代码方面的抽象设计、结构方面的、优秀的第三方扩展等。
相关文章
|
2月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
2月前
|
设计模式 算法 数据库连接
PHP中的设计模式:提高代码的可维护性与扩展性本文旨在探讨PHP中常见的设计模式及其应用,帮助开发者编写出更加灵活、可维护和易于扩展的代码。通过深入浅出的解释和实例演示,我们将了解如何使用设计模式解决实际开发中的问题,并提升代码质量。
在软件开发过程中,设计模式是一套经过验证的解决方案模板,用于处理常见的软件设计问题。PHP作为流行的服务器端脚本语言,也有其特定的设计模式应用。本文将重点介绍几种PHP中常用的设计模式,包括单例模式、工厂模式和策略模式,并通过实际代码示例展示它们的具体用法。同时,我们还将讨论如何在实际项目中合理选择和应用这些设计模式,以提升代码的可维护性和扩展性。
59 4
|
5月前
|
缓存 监控 前端开发
python开发中的技术选型与性能优化
在Python项目(一个基于Django和React的电商平台)开发中,面临技术选型、性能优化、架构设计和成本节约等问题。选用Django后端框架和React前端,利用Redux管理状态。为优化性能,进行了数据库索引优化、使用Redis缓存、异步处理(Celery)。采用微服务、RESTful API和代码复用提升架构灵活性。通过开源软件、云服务和注重代码质量降低成本。同时,借助日志记录、版本控制和监控工具有效排查故障。最终实现了一个稳定、高效且可扩展的平台。
71 1
|
6月前
|
网络安全 PHP
Laravel框架的性能优化
Laravel框架的性能优化
|
6月前
|
前端开发 JavaScript 开发者
实用技巧:提高前端开发效率的5个方法
提高前端开发效率是每个开发者都追求的目标。本文将介绍5个实用的技巧,帮助前端开发者提升工作效率:使用代码片段加速开发、合理利用浏览器开发者工具、充分利用现有框架和库、使用自动化构建工具、保持学习和不断优化工作流程。
|
6月前
|
JavaScript 前端开发 开发者
ES6引入了哪些新特性,你在游戏开发中如何利用这些特性提升代码质量?
ES6引入了块级作用域、箭头函数、解构赋值、默认参数、模板字符串和类等新特性,提升了JavaScript代码的可读性与效率。在游戏开发中,这些特性有助于管理状态、简化回调函数、方便数据提取、增强函数灵活性、优化字符串拼接及实现面向对象编程,从而改善代码质量,提高性能和可扩展性。不过,实际应用时需结合项目需求和编码规范。
36 3
|
缓存 安全 数据库
构建简单博客系统:从数据库设计到性能优化实践
本文以构建简单博客系统为例,详细介绍了从数据库设计与建模到性能调优与扩展的全过程。通过丰富的代码示例,读者将全面了解如何设计数据库模型、实现数据库操作和查询优化,以及如何实现用户认证与权限控制,最终实现一个高效、安全的博客系统。
281 0
|
缓存 编解码 前端开发
前端项目的性能优化实战
前端项目的性能优化实战
149 0
|
设计模式 安全 测试技术
经验总结 | 重构让你的代码更优美和简洁
最近,笔者有幸对高德打车订单Push项目进行了重构,与大家分享一下代码重构相关的工作经验,希望对大家有所启发。
229 1
经验总结 | 重构让你的代码更优美和简洁
|
缓存 JSON 前端开发
支付宝前端的代码结构及性能优化大总结
支付宝前端的代码结构及性能优化大总结
260 0
支付宝前端的代码结构及性能优化大总结
下一篇
无影云桌面