首页
归档
朋友
关于我
留言
推荐
Wiki知识库
Search
1
虚拟机无法ping不通百度,并无法访问浏览器
3,903 阅读
2
mysql使用or条件使索引失效
2,013 阅读
3
mysql如何在一对多查询时选取时间最近的一条记录
1,620 阅读
4
git常用命令大全
1,392 阅读
5
根据MySQL获取当天,昨天,本周,本月,上周,上月,本月的起始时间
1,381 阅读
PHP
面向对象
设计模式
代码片段
常用函数
数据库
MySQL
服务器
Docker容器
虚拟机
缓存知识
Nginx
前端
中间件
网络编程
工具软件
Git
杂乱无章
面试指南
MySQL面试
登录
Search
标签搜索
php
mysql
代码片段
linux
Redis
nginx
Thinkphp
mysql优化
docker
面向对象
git
Laravel框架
http协议
缓存
Redis性能优化
linux命令
编译安装
PhpSpreadsheet
导出excel
构造器
黎明强
累计撰写
64
篇文章
累计收到
24
条评论
首页
栏目
PHP
面向对象
设计模式
代码片段
常用函数
数据库
MySQL
服务器
Docker容器
虚拟机
缓存知识
Nginx
前端
中间件
网络编程
工具软件
Git
杂乱无章
面试指南
MySQL面试
页面
归档
朋友
关于我
留言
推荐
Wiki知识库
搜索到
13
篇与
代码片段
的结果
2022-07-25
批量处理的万级数据的优化方案
业务场景需求:一个【手机号码】的文本list ,需要导入到数据库中 (并在根据这个号码写入一些XX字段、判断一些手机号的一些校验、一些远程请求调用的服务~)一些棘手的问题查询订单在是否存在这个手机号,存在就标记查询订单是否有多次下单,存在再标记查询用户是否vip用户,是是标记根据“手机号”远程调用【用户微服务】接口等,校验用户的真实身份根据用户取到用户手机号,再把用户信息存储redis上........N次的 (20次的查询IO 操作),还做了Redis的查询读写操作...等等测试结果:测试 ,导致100条数据查询 ,都需要30秒。比如 需要处理1-10W的数据,然后批量添加,每个$v的都需要处理很多的sql 查询的IO操作,还要多次的http请求 跨服务的调用,大部分的耗时都卡在“数据库”层面,导致的。目前优化方案每个查询都尽量走索引、减少回表操作,能合理的用到覆盖索引批量拼接sql 一次处理,分批处理 (错误示例 不要再循环中处理每条inster/update 操作)持续优化方案目前执行的慢,原因是卡在DB的IO操作,每条查询都要走20次的查询!!!需要优化sql的连接池 (配置文件连接池根据业务情况设置大一些)看能不能减少多余的判断操作(DB的操作IO ,减少一条是一条)能不走DB ,就走redis,看能不能全走缓存层,去掉db层最好。如果不行,就试试把一些判断数据用布隆过滤器 去走redis的一级缓存大数据的情况下,如果数据是冷数据,不是热数据不经常操作,可以把数据全部存入clickhouse ,然后再查,速度快的一批,缺点:更新删除操作 重新建立树结构。
2022年07月25日
16 阅读
0 评论
0 点赞
2022-02-16
【转】代码的简洁之道
简介原文作者:Summer转自链接:https://learnku.com/laravel/t/62638版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请保留以上作者信息和原文链接。我最近遇到了 这条 Twitter,其中 @samuelstancl 列出了在 Laravel 中编写更干净代码的技巧, 以及一些通用的 Laravel 编码建议。 这些是培养对什么是好的代码和什么是坏的代码的感觉的一个很好的起点 - 所以我在下面整理了它们(带有代码示例),没有特定的顺序。细节决定成败干净的代码是微观层面不断做出正确决策的结果。使用表查找不要编写重复的 else if 语句,而是使用数组根据您拥有的键查找所需值。 代码将更清晰且更具可读性,如果出现问题,您将看到可以理解的异常。 没有半途而废的边缘情况。不好的// 不好的 if ($order->product->option->type === 'pdf') { $type = 'book'; } else if ($order->product->option->type === 'epub') { $type = 'book'; } else if ($order->product->option->type === 'license') { $type = 'license'; } else if ($order->product->option->type === 'artwork') { $type = 'creative'; } else if $order->product->option->type === 'song') { $type = 'creative'; } else if ($order->product->option->type === 'physical') { $type = 'physical'; } if ($type === 'book') { $downloadable = true; } else if ($type === 'license') { $downloadable = true; } else if $type === 'creative') { $downloadable = true; } else if ($type === 'physical') { $downloadable = false; }正确的// 正确的 $type = [ 'pdf' => 'book', 'epub' => 'book', 'license' => 'license', 'artwork' => 'creative', 'song' => 'creative', 'physical' => 'physical', ][$order->product->option->type]; $downloadable = [ 'book' => true, 'license' => true, 'creative' => true, 'physical' => false, ][$type];尽早返回通过尽早返回值来避免不必要的嵌套。过多的嵌套和 else 语句往往会使代码难以理解。槽糕写法// 糟糕的示例 if ($notificationSent) { $notify = false; } else if ($isActive) { if ($total > 100) { $notify = true; } else { $notify = false; } else { if ($canceled) { $notify = true; } else { $notify = false; } } } return $notify;推荐// 推荐的示例 if ($notificationSent) { return false; } if ($isActive && $total > 100) { return true; } if (! $isActive && $canceled) { return true; } return false;正确分割线不要在随机的地方分割线,但也不要让它们太长。 使用 [ 打开数组并缩进值往往效果很好。 与长函数参数值相同。 其他拆分行的好地方是链式调用和闭包。// 糟糕的示例 // 没有分行 return $this->request->session()->get($this->config->get('analytics.campaign_session_key')); // 无意义分行 return $this->request ->session()->get($this->config->get('analytics.campaign_session_key'));推荐// 推荐的示例 return $this->request->session()->get( $this->config->get('analytics.campaign_session_key') ); // 闭包 new EventCollection($this->events->map(function (Event $event) { return new Entries\Event($event->code, $event->pivot->data); })); // 数组 $this->validate($request, [ 'code' => 'string|required', 'name' => 'string|required', ]);不要创建没用的变量可以直接传值,就不要创建没用的变量。// 坏的 public function create() { $data = [ 'resource' => 'campaign', 'generatedCode' => Str::random(8), ]; return $this->inertia('Resource/Create', $data); }推荐// 好的 public function create() { return $this->inertia('Resource/Create', [ 'resource' => 'campaign', 'generatedCode' => Str::random(8), ]); }能提高可读性的时候再创建变量和上一条相反,有时候一个值来自一整套复杂的计算,因此创建一个变量,可以提高可读性,甚至连注释都省了。记住,上下文很重要,并且你编写代码的最终目标是让代码更具有可读性。// 坏的 Visit::create([ 'url' => $visit->url, 'referer' => $visit->referer, 'user_id' => $visit->userId, 'ip' => $visit->ip, 'timestamp' => $visit->timestamp, ])->conversion_goals()->attach($conversionData);推荐// 好的 $visit = Visit::create([ 'url' => $visit->url, 'referer' => $visit->referer, 'user_id' => $visit->userId, 'ip' => $visit->ip, 'timestamp' => $visit->timestamp, ]); $visit->conversion_goals()->attach($conversionData);根据业务逻辑来创建模型的方法控制器应该尽量保持简单。 比如以 “为订单创建发票” 这样的方式调用方法。调用方法不需要关心你的数据库表结构的细节,这些由模型自己内部实现。// 糟糕的方式 // 为订单创建发票 DB::transaction(function () use ($order) { Sinvoice = $order->invoice()->create(); $order—>pushStatus(new AwaitingShipping); return $invoice; });// 优雅的方式 $order->createInvoice();创建动作类让我们来继续刚才的例子。有时,可以为某个动作单独创建一个类,这样会使代码更加整洁。模型封装的业务逻辑可以基于动作类,但是记得动作类不可太大。// 糟糕的方式 public function createInvoice(): Invoice { if ($this->invoice()->exists()) { throw new OrderAlreadyHasAnInvoice('Order already has an invoice.'); } return DB::transaction(function () use ($order) { $invoice = $order->invoice()->create(); $order->pushStatus(new AwaitingShipping); return $invoice; }); }推荐// 优雅的方式 // 订单模型 public function createInvoice(): Invoice { if ($this->invoice()->exists()) { throw new OrderAlreadyHasAnInvoice('Order already has an invoice.'); } return app(CreateInvoiceForOrder::class)($this); } // 订单创建发票动作类 class CreatelnvoiceForOrder { public function _invoke(Order $order): Invoice { return DB::transaction(function () use ($order) { $invoice = $order->invoice()->create(); $order->pushStatus(new AwaitingShipping); return $invoice; }); } }考虑表单请求考虑使用表单请求,它们是隐藏复杂验证逻辑的好地方,但要注意这一点 — 隐藏的东西。当您的验证逻辑很简单时,在控制器中执行它并没有错,将其移至表单请求使其变得不那么明确。/** * 获取适用于请求的验证规则. * * @返回数组 */ public function rules() { return [ 'title' => 'required|unique:posts|max:255', 'body' => 'required', ]; }使用事件考虑将一些逻辑从控制器写到事件。例如,在创建模型时,好处是创建这些模型在任何地方 (控制器,任务,…) 并且控制器不必担心 DB 模式的细节。// 糟糕的示例 // 只在这个地方有效并关注它 // 模型应该关心的细节. if (! isset($data['name'])) { $data['name'] = $data['code']; } $conversion = Conversion::create($data);// 推荐的示例 $conversion = Conversion::create($data); // 模型 class ConversionGoal extends Model { public static function booted() { static::creating(function (self $model) { $model->name ??= $model->code; }); } }拆分方法如果某些方法太长或是太复杂,很难理解究竟做了什么,可以尝试将复杂的逻辑拆分成多个方法。public function handle(Request $request, Closure $next) { // 我们将三段逻辑分别提取成单独的方法 $this->trackVisitor(); $this->trackCampaign(); $this->trackTrafficSource($request); $response = $next($request); $this->analytics->log($request); return $response; }创建助手函数如果你多次重复一段代码,考虑一下将它们提取为助手函数是不是可以让代码更简洁。// app/helpers.php 文件,在 composer.json 中自动加载 function money(int $amount, string $currency = null): Money { return new Money($amount, $currency ?? config('shop.base_currency')); } function html2text($html = ''): string { return str_replace(' ', ' ', strip_tags($html)); }避免使用助手类有时候人们会使用类来归类助手函数(注意),可要小心了,这可能会让代码变得更混乱。常见的做法是定义一个只包含一个作为助手函数使用的静态方法的类。更好的做法是将这些方法放入具有具体逻辑的类中,或者是只将它们当做是全局函数。// 坏的 class Helper { public function convertCurrency(Money $money, string $currency): self { $currencyConfig = config("shop.currencies.$currency"); $decimalDiff = ... return new static( (int) round($money->baseValue() * $currencyConfig[value] * 10**$decimalDiff, 0), $currency ); } } // 使用 use App\Helper; Helper::convertCurrency($total, 'EUR');// 好的 class Money { // 其他的 money/currency 逻辑 public function convertTo(string $currency): self { $currencyConfig = config("shop.currencies.$currency"); $decimalDiff = ... return new static( (int) round($this->baseValue() * $currencyConfig[value * 10**$decimalDiff, 0), $currency ); } } // 使用 $EURtotal = $total->convertTo('EUR');拿出一个周末来学习 OO了解静态(static)/ 实例(instance)方法和变量,还有私有的(private)/ 保护的(protected)/ 公共的(public)之间的可见性的区别。还要了解 Laravel 如何使用魔法方法。当你是初学者的时候可能不会很常用,但是随着你的编码水平增长,这些是至关重要的。不要在类中只写过程代码这将前面的推文与此处的其他提示联系起来。OOP 的存在就是为了让你的代码更加具有可读性,请使用 OOP。不要再在控制器中写好几百行的过程代码了!!!。阅读 SRP 之类的内容,并进行合理的扩展避免使用那种处理很多和当前类不相关逻辑的类,但是也不要为每件事都创建一个类。你是为了写干净的代码,而不是想在每件事上都做分离。避免函数中参数过多当您看到具有大量参数的函数时,它可能意味着:该函数包含太多职责,应该分离。职责没问题,但你应该学会重构他的长签名.以下是修复第二种情况的两种策略使用数据传输对象 (DTO)与其以特定顺序传递大量参数,不如考虑创建一个具有属性的对象来存储这些数据。 如果您发现某些行为可以移入此对象,则可以加分。// 糟糕的示例 public function log($url, $route_name, $route_data, $campaign_code, $traffic_source, $referer, $user_id, $visitor_id, $ip, $timestamp) { // ... }// 推荐的示例 public function log(Visit $visit) { // ... } class Visit { public string $url; public ?string $routeName; public array $routeData; public ?string $campaign; public array $trafficSource[]; public ?string $referer; public ?string $userId; public string $visitorId; public ?string $ip; public Carbon $timestamp; // ... }创建流式对象你可以使用流式 API 来创建对象。使用单独的方法调用来逐渐添加数据,并且只要构造函数中的绝对最小值。正是因为每个方法都返回 $this ,你可以在任意一次调用后让整个流程停下来。Visit::make($url, $routeName, $routeData) ->withCampaign($campaign) ->withTrafficSource($trafficSource) ->withReferer($referer) // ... 等等使用自定义集合创建自定义集合可以更好地写出更富有表现力的语法。参考这个订单合计的示例:// 坏的 $total = $order->products->sum(function (OrderProduct $product) { return $product->price * $product->quantity * (1 + $product->vat_rate); });// 好的 $order->products->total(); class OrderProductCollection extends Collection { public function total() { $this->sum(function (OrderProduct $product) { return $product->price * $product->quantity * (1 + $product->vat_rate); }); } }不要使用缩写不要觉得很长的变量名 / 方法名就是不对的,才不是这样,它们很有表现力。使用一个长的方法名比短的更好,配合查阅文档能更完整地了解它的功能。变量也是如此。不要使用无意义的几个字母的缩写。// 坏的 $ord = Order::create($data); // ... $ord->notify();// 好的 $order = Order::create($data); // ... $order->sendCreatedNotification();尝试在控制器中只使用 CURD 动作如果可以的话,只使用控制器中的 7 个 CURD 动作,通常来说会更少。不要在控制器中创建 20 多个方法,更短的控制器更好一些。使用更具有表现力的方法名称考虑「这个对象可以完成什么事情」,而不是「这个对象能做什么」。也会有例外,比如操作类。这是个很好的经验。// 坏的 $gardener->water($plant); $orderManager->lock($order);// 好的 $plant->water(); $order->lock();创建单次使用的 trait将方法添加到它所属的类中,比为每件事都创建操作类简洁得多,但是这会让类变得很大。尝试使用特征 traits,它主要是为了代码复用,但是单次使用的 trait 并没有错。class Order extends Model { use HasStatuses; // ... } trait HasStatuses { public static function bootHasStatuses() { ... } public static $statusMap = [ ... ]; public static $paymentStatusMap = [ ... ]; public static function getStatusId($status) { ... } public static function getPaymentStatusId($status): string { ... } public function statuses() { ... } public function payment_statuses() { ... } public function getStatusAttribute(): OrderStatusModel { ... } public function getPaymentStatusAttribute(): OrderPaymentStatus { ... } public function pushStatus($status, string $message = null, bool $notification = null) { ... } public function pushPaymentStatus($status, string $note = null) { ... } public function status(): OrderStatus { ... } public function paymentStatus(): PaymentStatus { ... } }创建一次性引入类似于一次性 traits. 当您有一个很长的模板并且希望使其更易于管理时,这种策略非常有用。布局中的 @include-ing 页眉和页脚或页面视图中的复杂表单等都没有问题。导入命名空间而不是使用别名有时您可能有多个同名的类。 与其使用别名导入它们,不如导入命名空间。// 糟糕的示例 use App\Types\Entries\Visit as VisitEntry; use App\Storage\Database\Models\Visit as VisitModel; class DatabaseStorage { public function log(VisitEntry $visit) { $visitModel = VisitModel::create([ // ... ]); } }// 推荐的示例 use App\Types\Entries; use App\Storage\Database\Models; class DatabaseStorage { public function log(Entries\Visit $visit) { $visitModel = Models\Visit::create([ // ... ]); } }为 where() 创建查询方法使用更具有表现力的名字创建查询方法,而不是编写完整的 where()。这可以让你的代码(例如控制器)尽可能更少地与数据库结构产生耦合,并且可以让代码更清晰。// 不好的 Order::whereHas('status', function ($status) { return true$status->where('canceled', true); })->get();// 好的 Order::whereCanceled()->get(); class Order extends Model { public function scopeWhereCanceled(Builder $query) { return $query>whereHas('status', function ($status) { return $status->where('canceled', true); }); } }不要使用模型方法来检索数据如果你想要从模型中获取数据,可以创建一个访问器。保留以某种方式改变模型的方法。// 坏的 $user->gravatarUrl(); class User extends Authenticable { // ... public function gravatarUrl() { return "https://www.gravatar.com/avatar/" . md5(strtolower(trim($this->email))); } }// 好的 Suser->gravatar_url; class User extends Authenticable { // ... public function getGravatarUrlAttribute() { return "https://www.gravatar.com/avatar/" . md5(strtolower(trim($this->email))); } }使用自定义配置文件你可以在配置文件中存储类似「每页几条数据」这样的内容。不要直接将它们放在 app 配置文件中。创建你自己的配置文件。例如,在一个电商项目中,你可以使用 config/shop.php。// config/shop.php return [ 'vat rates' => [ 0.21, 0.15, 0.10, 0.0, ], 'fee_vat_rate' => 0.21, 'image_sizes' => [ 'base' => 500, // detail 't1' => 250, // index 't2' => 50, // search ], ];不要使用控制器命名空间使用可调用的数组语法 [PostController::class, 'index'] ,而不是直接写控制器和方法名 PostController@index。这样写的话你可以点击 PostController 来跳转到类的定义。// 坏的 Route::get('/posts', 'PostController@index');// 好的 Route::get('/posts', [PostController::class, 'index']);考虑使用单动作控制器如果你有复杂的路由操作,考虑将它放在单独的控制器中。对于 OrderController::create,你可以创建 CreateOrderController。另一个解决方法是将该逻辑转移到一个动作类 —— 在你的实际情况中选择最好用的方法。// 我们使用上面提到的类语法 Route::post('/orders/', CreateOrderController::class); class CreateOrderController { public function _invoke(Request $request) { // ... } }友好型 IDE安装扩展,编写注释,使用类型提示。 您的 IDE 将帮助您让您的代码正常工作,这让您可以将更多的精力花在编写可读的代码上。$products = Product::with('options')->cursor(); foreach ($products as $product) { /** @var Product $product */ if ($product->options->isEmpty()) { // ... } } /////////////////////////// foreach (Order::whereDoesntHave('invoice')->whereIn('id', $orders->pluck('id'))->get() as $order) { /** @var Order $order */ $order->createInvoice(); // ... } /////////////////////////// $productImage ->help('Max 2 MB') ->store(function (NovaRequest $request, ProductModel $product) { /** @var UploadedFile $image */ $image = $request->image; // ... });使用短运算符PHP 有很多很棒的操作符可以替代丑陋的 if 检查。 记住它们。// 糟糕的 // truthy test if (! $foo) { $foo = 'bar'; } // null test if (is_null($foo)) { $foo = 'bar'; } // isset test if (! isset($foo)) { $foo = 'bar'; }// 优雅的 // truthy test $foo = $foo ?: 'bar'; // null test $foo = $foo ?? 'bar'; // PHP 7.4 $foo ??= 'bar'; // isset test $foo = $foo ?? 'bar'; // PHP 7.4 $foo ??= 'bar';决定您是否喜欢运算符周围的空格在上面你可以看到我在 ! 和我要否定的值之间使用了空格。 我喜欢这个,因为它清楚地表明该值被否定了。 我在点周围做同样的事情。 可以按照您的喜好来清理您的代码。助手函数而不是 Facades考虑使用助手函数而不是 Facades 。 因为他们可以使得代码变得很整洁。 这在很大程度上取决于个人喜好,但调用全局函数而不是导入类并静态调用方法对我来说感觉更好。 session('key') 语法的加分项。// 糟糕的示例 Cache::get('foo');// 推荐的示例 cache()->get('foo'); // Better cache('foo');为业务逻辑创建自定义 Blade 指令你可以通过创建自定义指令来让你的 Blade 模板更具有表现力。举个例子,你可以使用 @admin 来检查用户是否是管理员,而不是使用具体的逻辑来判断。// 不好的写法 @if(auth()->user()->hasRole('admin')) // ... @else // ... @endif// 好的写法 @admin // ... @else // ... @endadmin避免在 Blade 中查询数据库可能有的时候你想要在 Blade 中查询数据库。有些情况下这是可以的,例如布局文件。但是如果是在视图文件中,则需要在控制器中向视图传入查询好的数据。// 不好的写法 @foreach(Product::where('enabled', false)->get() as $product) // ... @endforeach// 好的写法 // 控制器 return view('foo', [ 'disabledProducts' => Product::where('enabled', false)->get(), ]); // 视图 @foreach($disabledProducts as $product) // ... @endforeach使用精确的比较运算符始终使用严格比较(=== 和 !==)。 如果需要,在比较之前将事物转换为正确的类型。 比奇怪的 == 结果要好。 还要考虑在您的代码中启用严格类型。 这将防止将错误数据类型的变量传递给函数。// 糟糕的示例 $foo == 'bar';// 推荐的示例 $foo === 'bar'; // Better declare(strict_types=1);仅当他们澄清事情时才使用文档块很多人会不同意这一点,因为他们这样做了。 但这没有任何意义。 当它们不提供任何额外信息时,使用 文档是没有意义的。 如果类型提示足够了,就不要添加文档块,那样只是多余的。// 糟糕的示例 // 全部没有类型 function add_5($foo) { return $foo + 5; } // @param 注释精确地添加了 0% 值和 100% 噪声。 /** *给一个数加 5。 * * @param int $foo * @return int */ function add_5(int $foo): int { return $foo + 5; }// 推荐的示例 // 没有文档块,一切都清楚 function add_5(int $foo) { return $foo + 5; } //类型提示说得尽可能多,注释说得更多。 /** * 把单词变成句子。 * * @param string[] $words * @返回字符串 */ function sentenceFromWords(array $words): string { return implode(' ', $words) . '.'; } // 个人最爱。 只使用能带来价值的注解。 不要仅仅因为它太常见就使用 description 或 @return。 /** @param string[] $words */ function sentenceFromWords(array $words): string { return implode(' ', $words) . '.'; }验证规则有单一的真实来源如果你在多个地方验证某个资源的属性,你肯定希望将这些验证规则集中起来,这样你就不会在一个地方更改它们而忘记其他地方。 我经常发现自己在模型的方法中保留了验证规则。 这让我可以在任何需要的地方重用它们 —— 包括在控制器或表单请求中。class Reply extends Model { public static function getValidationRules(): array { return [ 'thread_id' => ['required', 'integer'], 'user_id' => ['required', 'integer'], 'body' => ['required', 'string', new SpamRule()], ]; } }可以使用集合来优化代码不要仅仅因为 Laravel 提供它们就将所有数组转换为集合,而是当您可以使用集合语法来优化代码时将数组转换为集合。$collection = collect([ ['name' => 'Regena', 'age' => null], ['name' => 'Linda', 'age' => 14], ['name' => 'Diego', 'age' => 23], ['name' => 'Linda', 'age' => 84], ]); $collection->firstWhere('age', '>=', 18);在对你有利的时候编写函数式代码函数式代码不仅能使代码整洁,还能降低代码的易读性。 将常见循环重构为函数调用,但不要为了避免编写循环而编写愚蠢复杂的 reduce ()。 两者都有一个用例。// 糟糕的示例 return array_unique(array_reduce($keywords, function ($result, $keyword) { return array_merge($result, array_reduce($this->variantGenerators, function ($result2, $generator) use ($keyword) { return array_merge($result2, array_map(function ($variant) { return strtolower($variant); }, $generator::getVariants($keyword))); }, [])); }, []));// 推荐的示例 return $this->items()->reduce(function (Money $sum, OrderItem $item) { return $sum->addMoney($item->subtotal()); }, money(0, $this->currency));注释通常表明代码设计不佳在撰写评论之前,问问自己是否可以重命名某些内容或创建变量以提高可读性。 如果这是不可能的,请以您和您的同事在 6 个月后都能理解的方式撰写评论。上下文问题上面我说将业务逻辑转移到逻辑类 / 服务类是好的。 但上下文很重要, 这是来自流行的 “Laravel 最佳实践” 存储库的代码设计建议。 绝对没有理由将 3 行检查放入类中。 这只是过度设计。// 糟糕的示例 public function store(Request $request) { $this->articleService->handleUploadedImage($request->file('image')); } class ArticleService { public function handleUploadedImage($image) { if (!is_null($image)) { $image->move(public_path('images') . 'temp'); } } }// 推荐的示例 public function store(Request $request) { if ($request->hasFile('image')) { $request->file('image')->move(public_path('images') . 'temp'); } // ... }只使用对你有帮助的东西,忽略其他一切您的目标是编写更具可读性的代码。您的目标不是按照某人在互联网上所说的那样去做。这些技巧只是有助于写出优雅的代码。 牢记你的最终目标并问自己「这样更好吗?」
2022年02月16日
44 阅读
1 评论
0 点赞
2021-04-22
PhpSpreadsheet实现导入&导出功能
前文导入excel的文本内容如下代码演示composer安装PhpSpreadsheet (这是新的Excel类库),不用旧的PHPExcel的类库,旧的已被官网弃用不维护了!!前端代码<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="./index5.php" method="post" enctype="multipart/form-data"> <input type="file" name="fileUpload" /> <input type="submit" value="上传文件" /> </form> </body> </html>后端代码 require_once 'vendor/autoload.php'; $filePath = $_FILES['fileUpload']['tmp_name']; $reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx'); $reader->setReadDataOnly(TRUE); $spreadsheet = $reader->load($filePath); //载入excel表格 $worksheet = $spreadsheet->getActiveSheet(); $highestRow = $worksheet->getHighestRow(); // 总行数 $highestColumn = $worksheet->getHighestColumn(); // 总列数 $highestColumnIndex = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($highestColumn); // e.g. 5 $lines = $highestRow - 1; if ($lines <= 0) { exit('Excel表格中没有数据'); } $import_data = []; for ($i = 2; $i <= $highestRow; $i++) { $import_data[$i]['id'] = $spreadsheet->getActiveSheet()->getCell("A" . $i)->getValue(); $import_data[$i]['nick_name'] = $spreadsheet->getActiveSheet()->getCell("B" . $i)->getValue(); $import_data[$i]['number'] = $spreadsheet->getActiveSheet()->getCell("C" . $i)->getValue(); $import_data[$i]['vip_number'] = $spreadsheet->getActiveSheet()->getCell("D" . $i)->getValue(); $import_data[$i]['card'] = $spreadsheet->getActiveSheet()->getCell("E" . $i)->getValue(); } $import_data = array_values($import_data);//重置索引 echo "<pre>"; print_r($import_data);exit; exit; 第二种方式不用$spreadsheet读取的文件的变量句柄,用 $worksheet获取所有的表格行数,先获取某一个sheet。 require_once 'vendor/autoload.php'; $filePath = $_FILES['fileUpload']['tmp_name']; $reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx'); $reader->setReadDataOnly(TRUE); $spreadsheet = $reader->load($filePath); //载入excel表格 $worksheet = $spreadsheet->getActiveSheet(); $highestRow = $worksheet->getHighestRow(); // 总行数 $highestColumn = $worksheet->getHighestColumn(); // 总列数 $highestColumnIndex = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($highestColumn); // e.g. 5 $lines = $highestRow - 1; if ($lines <= 0) { exit('Excel表格中没有数据'); } for ($row = 2; $row <= $highestRow; ++$row) { $temp = array( 'id' => $worksheet->getCellByColumnAndRow('1', $row)->getValue(), 'nick_name' => $worksheet->getCellByColumnAndRow('2', $row)->getValue(), 'number' => $worksheet->getCellByColumnAndRow('3', $row)->getValue(), 'vip_number' => $worksheet->getCellByColumnAndRow('4', $row)->getValue(), 'card' => $worksheet->getCellByColumnAndRow('5', $row)->getValue(), 'other' => $worksheet->getCellByColumnAndRow('6', $row)->getValue(), 'addr' => $worksheet->getCellByColumnAndRow('7', $row)->getValue(), ); $list[] = $temp; } echo "<pre>"; print_r($list);exit;这样就可以根据数据入数据库了导出 require_once 'vendor/autoload.php'; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; use PhpOffice\PhpSpreadsheet\Cell\DataValidation; use PhpOffice\PhpSpreadsheet\IOFactory; function exportExcel(){ //定义一个excel的header表头 $header = ['A1' => '序号', 'B1' => '姓名', 'C1' => '证件号', 'D1' => '张数', 'E1' => '证件类型', 'F1' => '省', 'G1' => '市', 'H1' => '区/镇', 'I1' => '类别', 'J1' => '设计人', 'K1' => '完成时间', 'L1' => '备注']; $list = [ ['id'=>1,'name'=>'黎明强','card'=>'13800','number'=>10], ['id'=>2,'name'=>'黎明','card'=>'13811','number'=>100], ['id'=>3,'name'=>'黎明强森','card'=>'13822','number'=>200], ['id'=>4,'name'=>'黎明强2','card'=>'13833','number'=>300], ['id'=>5,'name'=>'黎明强3','card'=>'13844','number'=>400], ]; $spreadsheet = new Spreadsheet(); $sheetMain = $spreadsheet -> getsheet(0);//主sheet $sheetMain -> setTitle('人员信息');//设置sheet的名称 $sheetMain -> getPageSetup() -> setHorizontalCentered(true); $sheetMain -> getPageSetup() -> setVerticalCentered(false); foreach ($header as $key => $value) { $sheetMain -> setCellValue($key, $value); } $row = 2; //从第二行开始 foreach ($list as $item) { $column = 1; foreach ($item as $value) { $sheetMain->setCellValueByColumnAndRow($column, $row, $value); $column++; } $row++; } //定义文件名称,需要带有定义的后缀名 $filename = 'test.xlsx'; ob_end_clean(); //清除缓冲区,避免乱码 //将输出重定向到客户端的web浏览器 header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); header('Content-Disposition: attachment;filename="' . $filename . '"'); header('Cache-Control: max-age=0'); //如果浏览器为IE9 header('Cache-Control: max-age=1'); //如果通过SSL向IE提供服务 header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: cache, must-revalidate');//HTTP/1.1 header('Pragma: public');//HTTP/1.0 $writer = IOFactory ::createWriter($spreadsheet, 'Xlsx'); //指定格式 $writer -> save('php://output'); exit; }运行exportExcel();
2021年04月22日
423 阅读
2 评论
0 点赞
2020-11-11
array_filter()、array_map()、array_walk()三者的用法和区别
用法 array_filter() 重点在于过滤(而不是新增)某个元素,当你处理到一个元素时,返回过滤后的数组 array_map() 重点在于遍历一个数组或多个数组的元素,返回一个新的数组 array_walk() 重点在于遍历数组进行某种操作区别array_filter() 和 array_walk()对一个数组进行操作,数组参数在前,函数参数在后array_map() 可以处理多个数组,因此函数参数在前,数组参数在后,可以根据实际情况放入多个数组参数array_filter() 该函数把输入数组中的每个键值传给回调函数。如果回调函数返回 true,则把输入数组中的当前键值返回结果数组中。数组键名保持不变。【如果没callback 就默认过滤false,空字符串空数组等】//没有回调函数的情况下,可以过滤false、空等数组 $arr =[1,2,false,0,'',3,4]; $new_arr = array_filter($arr); var_dump($arr); /*array(4) { [0]=> int(1) [1]=> int(2) [5]=> int(3) [6]=> int(4) }*/ $arr = ['a','b',1,2,3]; $new_arr = array_filter($arr,function($val){ return is_numeric($val); }); var_dump($new_arr); //返回结果 //array (size=3) // 2 => int 1 // 3 => int 2 // 4 => int 3array_map()该函数将用户自定义函数作用到数组中的每个值上,并返回用户自定义函数作用后的带有新值的数组。可以传递多个数组,回调函数接受的参数数目应该和传递给 array_map() 函数的数组数目一致。$arr1 = [1,2,3,4,5]; $arr2 = [6,7,8,9,10]; //函数写前面,数组参数写后面 (callback后面可以传入多个数组....) $new_arr = array_map(function($val1,$val2){ return $val1 + $val2; },$arr1,$arr2); var_dump($new_arr); //返回结果 //array (size=5) // 0 => int 7 // 1 => int 9 // 2 => int 11 // 3 => int 13 // 4 => int 15array_walk()对数组中的每个元素应用用户自定义函数//将数组中的元素用于某种操作 $arr = ['a','b','c']; array_walk($arr,function($val,$key){ echo "{$key} is {$val} <br/>"; }); //返回结果 //0 is a //1 is b //2 is c //改变数组中的值,传参的时候使用引用 array_walk($arr,function(&$val,$key){ $val .= $val; }); var_dump($arr); //array (size=3) // 0 => string 'aa' (length=2) // 1 => string 'bb' (length=2) // 2 => string 'cc' (length=2)
2020年11月11日
824 阅读
0 评论
0 点赞
2020-11-10
php替换富文本内容的所有img的链接或者其他内容
第一种 :img标签 追加绝对路径$httpUlr = 'http://www.xxx.com/'; $str = '<p>放假啊少打飞机啊速度开了个家里卡圣诞节</p> <p> </p> <p><img src="/uploads/20190326/cad4dcb78fca9e98cf1c45553f0a1a99.jpg" alt="" width="100" height="100" /></p>'; preg_replace('/src="\//', 'src="'."$httpUlr".'/', $str);替换src的内容以及链接$text = '内容<img src="http://www.test.com/test1.jpg">内容'; $replaceSrc = 'http://www.test.com/test2.jpg';//要替换的src echo preg_replace('#<img(.+?)src="([^"]+?)"([^>]*?)>#',"<img$1src=\"$replaceSrc\"$3>",$text); //内容<img src="http://www.test.com/test2.jpg">内容
2020年11月10日
641 阅读
0 评论
0 点赞
2020-11-10
日期统计表数据,mysql没数据的日期进行补充日期
统计用到的日期,不用foreach循环得到日期再查N天的N条sql,效率比较低换一种方法,查一遍sql得出日期,比如一个月就 1-2 ,1-3,1-5才有数据,但统计要展示1-30号,将其他没数据补充日期,数据填充0注意的是:先查询得到sql数据,再补充日期,而不是得到日期再循环体内操作mysql得到统计。比如30天就循环30条sql 非常不友好第一步 $month = Request::param('month', date('Y-m')); $startMonth = date("Y-m-01", strtotime( $month)); $endMonth = date("Y-m-d", strtotime($month));第二步(得到数据) $view = self::alias('m') ->join('custom c','c.message_no=m.message_no','left') ->join('ts_custom ts','ts.message_no=c.message_no') ->where('follow_stage','in',[8,9,10,11,12,13]) ->whereRaw("DATE_FORMAT( ts.done_time, '%Y-%m-%d' ) BETWEEN "."'$startMonth ' and ". "'$endMonth'") ->field($field); $log = $view->group("DATE_FORMAT(ts.done_time, '%Y-%m-%d')")->order('date asc')->select()->toArray();第三步$str = ['date'=>'','follow_stage_8'=>'']; DataFillDate($log,[$startMonth,$endMonth],$str); /* * * 填补输出数据(天) * @param $log array 数据 (先查询好数据再操作如下) * @param $arr_time array 时间区间 * @param $ex_path array 数据结构 * */ function DataFillDate($log,$arr_time = [],$str = []) { $i = 0; $j = 0; $dateL = []; $date[0] = $arr_time[0]; //将需要的开始跟结束日期,进行并且得出需要多少天 ,比如 2020-1-3 ~ 2020-1-31 。中间相差28天。 $stimestamp = strtotime($arr_time[0]); $etimestamp = strtotime($arr_time[1]); // 计算日期段内有多少天 $days = ($etimestamp-$stimestamp)/86400+1; // 保存每天日期 $date = array(); //循环得到的日期 for($i=0; $i<$days; $i++){ $date[] = date('Y-m-d', $stimestamp+(86400*$i)); } //mysql得到的日期条数 foreach ($log as $key => $val){ $dateL[$key] = $val['date']; } //进行差异比较 count($dateL) > 0 ? $c = array_diff($date,$dateL) : $c = $date; //这一段进行数据填充0 $strR = []; foreach ($c as $k => $kv){ foreach ($str as $s => $sv){ $strR[$k][$s] = $s =='date' ? $kv : '0'; } } $res = array_merge($strR,$log); foreach ($res as $r => $rv){ $key = substr($rv['date'],0,4).substr($rv['date'],5,2).substr($rv['date'],8,10); $dd[$key] = $rv; } //再排序 rsort($dd); return $dd; }
2020年11月10日
632 阅读
0 评论
0 点赞
2020-11-10
PHP设置脚本最大执行时间的三种方法
php.ini 中缺省的最长执行时间是 30 秒,这是由 php.ini 中的 max_execution_time 变量指定,如果脚本需要跑很长时间,例如要大量发送电子邮件,或者分析统计大量数据,服务器会在 30 秒后强行中止正在执行的程序,这种情况就要更改php脚本最大执行时间。PHP设置脚本最大执行时间的三种方法在php.ini里面设置max_execution_time = 120;通过PHP的ini_set函数设置ini_set("max_execution_time", "120"); 设置120秒的意思通过set_time_limit 函数设置set_time_limit(120);以上几个数字设置为0则无限制,脚本会一直执行下去,直到执行结束。所以,需要长时间执行的脚本,一般在php代码开头处添加如下代码就可以了 set_time_limit(0);注意:方法一主要适用于网站已经做好了,后期维护人员对代码结构不熟悉,方法二和三适用于写代码时候,不过不推荐使用方法二,因为要注意ini_set函数是否会被禁用
2020年11月10日
790 阅读
0 评论
0 点赞
2020-11-10
PHP用户名用星号(*)处理或者手机号隐藏某一段
手机号星号屏蔽(简单版)//函数 【替换字符串的子串】 $phone = '15625599765'; //从第三位截取5个变成* $phone = substr_replace($phone,'***',3,5); echo $phone; //输出 : 156***765 //函数 【返回字符串的子串】 $mobile = '13912345678'; $newMobile1 = substr($mobile, 0, 5).'****'.substr($mobile, 9); echo $newMobile1; //13912****78 //正则 $mobile = '15625599765'; //d1 :首位长度 ,d2 尾部长度 $newMobile3 = preg_replace('/(\d{3})\d{4}(\d{2})/', '$1****$2', $mobile); echo $newMobile3; //156****9765用户名字星号屏蔽(支持UFT-8跟GB2312)// UTF-8中文正则:"/[\x{4e00}-\x{9fa5}]+/u" // GB2312中文正则:"/[".chr(0xa1)."-".chr(0xff)."]+/" $str = '黎明强'; //输出黎*强 $str ='黎明'; //黎* //判断是否包含中文字符 if(preg_match("/[\x{4e00}-\x{9fa5}]+/u", $str)) { //按照中文字符计算长度 $len = mb_strlen($str, 'UTF-8'); //echo '中文'; if($len >= 3){ //三个字符或三个字符以上掐头取尾,中间用*代替 $str = mb_substr($str, 0, 1, 'UTF-8') . '*' . mb_substr($str, -1, 1, 'UTF-8'); } elseif($len == 2) { //两个字符 $str = mb_substr($str, 0, 1, 'UTF-8') . '*'; } } else { //按照英文字串计算长度 $len = strlen($str); //echo 'English'; if($len >= 3) { //三个字符或三个字符以上掐头取尾,中间用*代替 $str = substr($str, 0, 1) . '*' . substr($str, -1); } elseif($len == 2) { //两个字符 $str = substr($str2, 0, 1) . '*'; } } echo $str;
2020年11月10日
1,019 阅读
0 评论
0 点赞
1
2