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);
    }


总结


  • 代码优化是一个不断优化的过程,好久没有更新文章啦,最近会更新一波代码简洁之道和性能优化的文章,包括代码方面的抽象设计、结构方面的、优秀的第三方扩展等。
相关文章
|
5月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
5月前
|
设计模式 算法 数据库连接
PHP中的设计模式:提高代码的可维护性与扩展性本文旨在探讨PHP中常见的设计模式及其应用,帮助开发者编写出更加灵活、可维护和易于扩展的代码。通过深入浅出的解释和实例演示,我们将了解如何使用设计模式解决实际开发中的问题,并提升代码质量。
在软件开发过程中,设计模式是一套经过验证的解决方案模板,用于处理常见的软件设计问题。PHP作为流行的服务器端脚本语言,也有其特定的设计模式应用。本文将重点介绍几种PHP中常用的设计模式,包括单例模式、工厂模式和策略模式,并通过实际代码示例展示它们的具体用法。同时,我们还将讨论如何在实际项目中合理选择和应用这些设计模式,以提升代码的可维护性和扩展性。
112 4
|
8月前
|
缓存 监控 前端开发
python开发中的技术选型与性能优化
在Python项目(一个基于Django和React的电商平台)开发中,面临技术选型、性能优化、架构设计和成本节约等问题。选用Django后端框架和React前端,利用Redux管理状态。为优化性能,进行了数据库索引优化、使用Redis缓存、异步处理(Celery)。采用微服务、RESTful API和代码复用提升架构灵活性。通过开源软件、云服务和注重代码质量降低成本。同时,借助日志记录、版本控制和监控工具有效排查故障。最终实现了一个稳定、高效且可扩展的平台。
103 1
|
3月前
|
前端开发 JavaScript 开发者
前端开发的终极技巧:如何让你的代码既简洁又高效,还能减少bug?
【10月更文挑战第30天】前端开发充满挑战与创新,如何编写简洁高效且少bug的代码是开发者关注的重点。本文介绍五大技巧:1. 模块化,提高代码复用性;2. 组件化,降低代码耦合度;3. 使用现代框架,提高开发效率;4. 统一代码规范,降低沟通成本;5. 利用工具,优化代码质量。掌握这些技巧,让前端开发更高效。
173 1
|
6月前
|
缓存 监控 中间件
构建高效的Go语言Web服务器:基于Fiber框架的性能优化实践
在追求极致性能的Web开发领域,Go语言(Golang)凭借其高效的并发处理能力、垃圾回收机制及简洁的语法赢得了广泛的青睐。本文不同于传统的性能优化教程,将深入剖析如何在Go语言环境下,利用Fiber这一高性能Web框架,通过精细化配置、并发策略调整及代码层面的微优化,构建出既快速又稳定的Web服务器。通过实际案例与性能测试数据对比,揭示一系列非直觉但极为有效的优化技巧,助力开发者在快节奏的互联网环境中抢占先机。
|
9月前
|
网络安全 PHP
Laravel框架的性能优化
Laravel框架的性能优化
|
8月前
|
设计模式 缓存 前端开发
现代PHP开发中的设计模式应用与性能优化
本篇文章深入探讨了PHP开发中设计模式的实际应用及其对性能的影响。通过分析具体案例和最新研究成果,文章揭示了合理运用设计模式不仅可以提升代码的可维护性和扩展性,还能在特定场景下优化性能。我们将一起探索如何通过科学方法将设计模式融入日常开发实践,同时保持代码的高效执行。
|
9月前
|
缓存 数据处理 数据库
Django 框架高级进阶:探索最佳实践与性能优化
【5月更文挑战第18天】在Django开发中,掌握高级技巧和性能优化是关键。最佳实践包括合理组织代码结构、数据库设计优化、使用信号机制和缓存策略。性能优化涉及数据库查询优化(如select_related和prefetch_related)、异步任务处理(如Celery)、启用HTTP缓存、优化模板渲染和服务器配置调整。示例中,通过分页减少数据加载量以提高性能。不断探索和应用这些方法能提升用户体验,应对高并发和大规模数据挑战。
152 6
|
8月前
|
安全 编译器 PHP
PHP 8 新特性详解:更高效、更简洁的编程体验
本文详细介绍了PHP 8的新特性及其对开发者日常工作的影响。通过分析这些新特性,读者可以更好地理解PHP 8相较于之前版本的改进之处,并在实际项目中加以应用。
|
9月前
|
前端开发 JavaScript 开发者
实用技巧:提高前端开发效率的5个方法
提高前端开发效率是每个开发者都追求的目标。本文将介绍5个实用的技巧,帮助前端开发者提升工作效率:使用代码片段加速开发、合理利用浏览器开发者工具、充分利用现有框架和库、使用自动化构建工具、保持学习和不断优化工作流程。

热门文章

最新文章