使用laravel开发了一套系统,并使用eloquent作为orm框架,查看sql日志发现执行了大量的重复sql,是否存在专业工具可以分析出这些不必要的sql,进行针对性的优化? 比如对慢查询日志来讲,有mysqldumpslow这个工具可以分析,那么对于自己导出的sql日志来讲,有什么分析工具吗?或者我应该结合什么工具和sql日志的输出格式来达成sql分析的目的?
我有一个api,需要在控制器中获取用户信息 这个api需要在登录 或者 不登录的情况下都能访问 如果我不加上 auth:sanctum 中间件则无法通过Auth::check()来验证用户是否登录 没有加上中间件 auth:sanctum , Auth::check() 方法始终返回的是false 我想在控制器中来验证用户信息而不是在中间件中验证直接返回401 望大佬解答
写文章的背景:最近失业在家,每天去图书馆阅读大量的书籍。 名句: 人这一生机会很多,重要的机会只有一个 创业成功绝对不是靠技术,而是靠市场 巨人公司的故事、企业助穷、Apple VS 诺亚机、微软如何崛起 如何减压 And 对抗焦虑 平面构成(对称与平衡,对比与调和) 情商课 1. 关切对方(就像父母对话孩子一样的温暖,天气冷不冷啊) 2. 学会道歉,有没有吓到你了 (对不起给你添麻烦了) 3. 共鸣 (那我们把空调开起来吧) 如下代码: namespace App\Http\Controllers\Api\Air\v_1_0; use App\Http\Controllers\Core\ApiController; use App\Models\LoginLog; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; use OSS\OssClient; class FileController extends ApiController { public function uploadFile(Request $request) { $file = $request->file(); $ret = $this->_upload($file); echo json_encode($ret); exit; } protected function _upload($file) { $dir_name = empty($_GET['dir']) ? 'image' : trim($_GET['dir']); $storage_path = config('upload.storage'); $webpath_path = config('upload.webpath'); $image_path = $dir_name . '/' . date("Y-m") . '/'; $app_url = env('APP_URL') . '/'; if (!$file) { return array('error'=>1,'message'=>"文件上传失败,请检查后重试"); } $ossConfig = config('oss.' . config('upload.ossflag')); if($ossConfig['bucket_addr']) { $app_url = $ossConfig['bucket_addr']; } foreach ($file as $k=>$v) { $tmpName = $v->getPathName(); $fileExtension = $v->getClientOriginalExtension(); $filePath = md5_file($tmpName) . '.' . $fileExtension; $file_size = $v->getSize(); if($file_size > config('upload.maxSize')) { return array('error'=>1,'message'=>"文件不可以超过50MB"); } if (!in_array(strtolower($fileExtension),config('upload.allowExts'))) { return array('error'=>1,'message'=>"文件类型不支持"); } //上传成功后处理 if(config('oss.' . 'oss_open') == 1) { $oss = new OssClient($ossConfig['access_key_id'], $ossConfig['access_key_secret'], $ossConfig['endpoint']); $res = $oss->uploadFile($ossConfig['bucket'],$webpath_path . $image_path . $filePath,$v->getPathName()); if ($res) { $data['oss'] = 1; $data['attach_url'] = $ossConfig['bucket_addr'] . $webpath_path . $image_path . $filePath; } else { $data['attach_url'] = $app_url . $webpath_path . $image_path . $filePath; $data['oss'] = 0; } $data['time'] = time(); $attach_id = DB::table('attachment')->insertGetId($data); $attach_array[] = $attach_id; $attach_url[] = $data['attach_url']; } else { $data['oss'] = 0; $data['attach_url'] = $app_url. $webpath_path . $image_path . $filePath; $data['time'] = time(); $attach_id = DB::table('attachment')->insertGetId($data); $attach_array[] = $attach_id; $attach_url[] = $data['attach_url']; } if(!$data['oss'] || config('oss.' . 'is_delete') != 1) { $v->move($storage_path . $image_path, $filePath); unset($data); } return array('error'=>0,'attach'=>$attach_array,'url'=>$attach_url[0],'url_arr'=>$attach_url); } } }
之前团队留下的模型代码都是这样的: public function getcount($where){//$where是数组,严格对应DB字段! if(!empty($where)){ $db->where($where); } $db->from($tablename); return $db->count_all_results(); } public function edit($tid,$data){//$data是数组,严格对应DB字段! $db->where('tid', $tid); return $db->update($tablename, $data); } 当我在控制器里调用MODEL的时候,要严格按照数据库schema来拼装参数。 MODEL里这样写这样的纯粹的增删改查有什么优点? 验证部分究竟应该放在哪里比较好? 请阐明其中的道理和厉害关系。 之前可能有人看的太快导致的误解,上述代码的model里面那个empty并不算真的判断,关于$where参数,是需要严格按照数据库对应的字段拼装,而model里并没有任何判断,严重依赖控制器喂给它正确的参数。 项目经理铁定要求在控制器处理验证,所以现在的控制器里有数量可观的巨型函数(需要调用model的地方都超过150行了),而且格式基本近似。 举个例子,现在的情况是: view提交表单到controller,在那里验证表单,呼叫model,并为model拼装数组参数(如同上面代码里那样)。 看到后来新的答案里有些说的情况比较模糊,比如“复杂逻辑”这样还是没法明确。所以,我再细化一下问题: 现在的model里仅仅是转发了一下sql的增删改查的基本操作,在控制器里调用这些model的时候,我必须严格根据数据库里的字段名来拼装$data数组,这就基本等同于没有model,还不如控制器里直接拼sql算了。
laravel 代码混淆 1. 下载yakpro-po最新版 https://github.com/pk-fr/yakpro-po 2. 解压 unzip yakpro-po.zip -d /usr/local/ 3.给yakpro-po.php添加执行权限 cd /usr/local/yakpro-po chmod a+x yakpro-po.php 4.下载PHP-Parser4.x并解压到/usr/local/yakpro-po下 https://github.com/nikic/PHP-Parser/tree/4.x/ 如下图 https://segmentfault.com/img/bVdcpIP 5.创建yakpro-po软链接 ln -s /usr/local/yakpro-po/yakpro-po.php /usr/local/bin/yakpro-po 6.测试 yakpro-po --help 7. 假设我们有一个项目在/www/laravel_demo,不混淆vendor目录,不需要将其放进去 yakpro-po /www/laravel_demo/ -o /www/laravel_demo2/ --no-obfuscate-function-name --no-obfuscate-class_constant-name --no-obfuscate-class-name --no-obfuscate-interface-name --no-obfuscate-trait-name --no-obfuscate-property-name --no-obfuscate-method-name --no-obfuscate-namespace-name --no-obfuscate-label-name 说明: 以上--no-obfuscate-xxxx-name 表示不混淆的选项,推荐使用以上配置否则很容易出现混淆后不能正常访问的情况,这些配置也可以写到/usr/local/yakpro-po/yakpro-po.cnf中去,将对应的选项改为false即可,各个不同版本的laravel或者其他框架这个配置项也可能不同根据自己的改就行了 8.混淆后的代码在/www/laravel_demo2/yakpro-po/obfuscated中,是否可以只生成想要的源码大家可以查看官方文档,这是混淆后的源码,除php文件外其他文件会被原样复制过去 https://segmentfault.com/img/bVdcpJi 9. 加上vendor 目录,测试项目是否正常 10.可以参考下面的shell脚本 #!/bin/bash 注意yakpro-po需要已正常配置 #https://github.com/pk-fr/yakpro-po 设置目标位置 TARGET="/www/wwwroot/api6" 设置混淆后的代码位 OBFUSCATED_DIR="${TARGET}/yakpro-po/obfuscated" 检查yakpro-po是否存在 if ! command -v yakpro-po &> /dev/null; then echo "Error: 请检查yakpro-po是否正确安装!" exit 1 fi if [ -d "$TARGET" ]; then rm -rf "$TARGET" fi 执行混淆操作 yakpro-po /www/wwwroot/api/ -o "$TARGET" --no-obfuscate-property-name # 移动混淆后的代码到目标文件夹 mv $OBFUSCATED_DIR/* "$TARGET" #删除混淆目录 rm -rf "$TARGET/yakpro-po" 检查vendor压缩文件是否存在 if ! [ -f $TARGET/vendor*.zip ]; then echo "Error: 未找到vendor依赖库,请确认!" exit 1 fi 解压vendor目录到目标位置 unzip -o "$TARGET/vendor*.zip" -d "$TARGET" # 指定解压到$TARGET目录 echo "混淆成功,请检查项目是否正常运行"
堆是什么? 堆是基于树抽象数据类型的一种特殊的数据结构,用于许多算法和数据结构中。一个常见的例子就是优先队列,还有排序算法之一的堆排序。这篇文章我们将讨论堆的属性、不同类型的堆以及 堆的常见操作。另外我们还将学习堆排序,并将使用SPL实现堆。 根据定义,堆是一个拥有堆特性的树形数据结构。如果父节点大于子节点,那么它被称为最大堆,如果父节点小于子节点,则称为最小堆。下图是最大堆的例子 "https://wmprod.oss-cn-shanghai.aliyuncs.com/community/1724815173092_mNCz.png" (https://wmprod.oss-cn-shanghai.aliyuncs.com/community/1724815173092_mNCz.png) 我们看根节点,值100大于两个子节点19和36。对于19来说,该值大于17和3。其他节点也适用相同的规则。我们可以看到,这棵树没有完全排序。但重要的事实是我们总能找到树的最大值或最小值,在许多特殊的情况下这是非常有用的。 堆结构有很多种,如二叉堆、B堆、斐波那契堆、三元堆,树堆、弱堆等。二叉堆是堆实现中最流行的一种。二叉堆是一个完全二叉树(不了解二叉树的朋友可以看PHP实现二叉树),树的所有内部节点都被完全填充,最后一层可以完全填充的或部分填充。对于二叉堆,我们可以在对数时间复杂度内执行大部分操作。 堆的操作 堆是一个特殊的树数据结构。我们首先根据给定的数据构建堆。由于堆有严格的构建规则,所以我们每一步操作都必须满足这个规则。下面是堆的一些核心操作。 创建堆 插入新值 从堆中提取最小值或最大值 删除一个值 交换 从给定的项或数字集合创建堆需要我们确保堆规则和二叉树属性得到满足。这意味着父节点必须大于或小于子节点。对于树中的所有节点,都需要遵守这个规则。同样,树必须是一个完全的二叉树。在创建堆时,我们从一个节点开始,并向堆中插入一个新节点。 当插入节点操作时,我们不能从任意节点开始。插入操作如下 将新节点插入堆的底部 检查新节点和父节点的大小顺序,如果它们是正确的顺序,停止。 如果它们不是正确的顺序,交换它们然后继续前一步的检查。这一步骤与前一步一起被称为筛分或上升,等等。 提取操作(最小或最大)即从堆中取出根节点。在此之后,我们必须执行下列操作以确保剩余节点然仍符合堆的特点。 从堆移动最后一个节点作为新根 将新根节点与子节点进行比较,如果它们处于正确的顺序,则停止。 如果不是,则将根节点与子节点交换(当是小根堆时为最小子节点,当大根堆时为最大子节点)并继续前面的步骤。这一步与前一个步骤一起被称为下堆。 在堆中,一个重要的操作是交换。现在我们将使用PHP7来实现二叉堆。 namespace DataStructure\Heap; class MaxHeap { public $heap; public $count; public function __construct(int $size) { //初始化堆 $this->heap = array_fill(0, $size, 0); $this->count = 0; } public function create(array $arr = []) { array_map(function($item){ $this->insert($item); }, $arr); } public function insert(int $data) { //插入数据操作 if ($this->count == 0) { //插入第一条数据 $this->heap[0] = $data; $this->count = 1; } else { //新插入的数据放到堆的最后面 $this->heap[$this->count++] = $data; //上浮到合适位置 $this->siftUp(); } } public function display() { return implode(" ", array_slice($this->heap, 0)); } public function siftUp() { //待上浮元素的临时位置 $tempPos = $this->count - 1; //根据完全二叉树性质找到副节点的位置 $parentPos = intval($tempPos / 2); while ($tempPos > 0 && $this->heap[$parentPos] heap[$tempPos]) { //当不是根节点并且副节点的值小于临时节点的值,就交换两个节点的值 $this->swap($parentPos, $tempPos); //重置上浮元素的位置 $tempPos = $parentPos; //重置父节点的位置 $parentPos = intval($tempPos / 2); } } public function swap(int $a, int $b) { $temp = $this->heap[$a]; $this->heap[$a] = $this->heap[$b]; $this->heap[$b] = $temp; } public function extractMax() { //最大值就是大跟堆的第一个值 $max = $this->heap[0]; //把堆的最后一个元素作为临时的根节点 $this->heap[0] = $this->heap[$this->count - 1]; //把最后一个节点重置为0 $this->heap[--$this->count] = 0; //下沉根节点到合适的位置 $this->siftDown(0); return $max; } public function siftDown(int $k) { //最大值的位置 $largest = $k; //左孩子的位置 $left = 2 * $k + 1; //右孩子的位置 $right = 2 * $k + 2; if ($left count && $this->heap[$largest] heap[$left]) { //如果左孩子大于最大值,重置最大值的位置为左孩子 $largest = $left; } if ($right count && $this->heap[$largest] heap[$right]) { //如果右孩子大于最大值,重置最大值的位置为左孩子 $largest = $right; } //如果最大值的位置发生改变 if ($largest != $k) { //交换位置 $this->swap($largest, $k); //继续下沉直到初始位置不发生改变 $this->siftDown($largest); } } } 复杂度分析 因为不同种类的堆有不同的实现,所以各种堆实现也有不同的复杂度。但是有一个堆的操作在各类实现中都是O(1)的复杂度,就是获取最大值或者最小值。我看来看下二分堆的复杂度分析。 操作 平均复杂度 最坏复杂度 Search O(n) O(n) Insert O(1) O(log n) Delete O(log n) O(log n) Extract O(1) O(1) 因为二叉堆不是完全排序的,所以搜索操作会比二叉搜索树花更多的时间。 堆与优先队列 一个最常用的操作就是将堆当作优先队列来使用。在PHP实现栈和PHP实现队列中,我们已经了解到优先队列是一种根据元素权重而不是入队顺序来进行出队操作的结构。我们已经用链表实现优先队列和Spl实现优先队列,现在我们使用堆来实现优先队列。 namespace DataStructure\Heap; class PriorityQueue extends MaxHeap { public function __construct(int $size) { parent::__construct($size); } public function enqueue(int $val) { parent::insert($val); } public function dequeue() { return parent::extractMax(); } } 堆排序 在堆排序中,我们需要用给定的值构建一个一个堆。然后连续的检查堆的值以确保任何时候整个堆都是排序的。在正常的堆结构中,我们每当插入一个新的值到合适位置之后就停止检查,但是在堆排序中,只要有下一个值,我们就不断的去检查构建堆。伪代码如下: HeapSort(A) BuildHeap(A) for i = n-1 to 0 swap(A[0],A[i]) n = n - 1 Heapify(A, 0) BuildHeap(A) n = elemens_in(A) for i = floor(n / 2) to 0 Heapify(A, i) Heapify(A, i) left = 2i+1; right = 2i + 2; max = i if (left A[i]) max = left if (right A[max]) max = right if (max != i) swap(A[i], A[max]) Heapify(A, max) 从上面的伪代码可以看到,堆排序的第一步就是构建一个堆。每次我们向堆中添加新的元素,我们都调用heapify来满足堆的特性。一旦堆构建好之后,我们对所有的元素都进行检查,下面使用PHP的实现堆排序。完整的代码可以点这里查看。 function heapSort(&$arr) { $length = count($arr); buildHeap($arr); $heapSize = $length - 1; for ($i = $heapSize; $i >= 0; $i--) { list($arr[0], $arr[$heapSize]) = [$arr[$heapSize], $arr[0]]; $heapSize--; heapify(0, $heapSize, $arr); } } function buildHeap(&$arr) { $length = count($arr); $heapSize = $length - 1; for ($i = ($length / 2); $i >= 0; $i--) { heapify($i, $heapSize, $arr); } } function heapify(int $k, int $heapSize, array &$arr) { $largest = $k; $left = 2 * $k + 1; $right = 2 * $k + 2; if ($left <= $heapSize && $arr[$k] < $arr[$left]) { $largest = $left; } if ($right <= $heapSize && $arr[$largest] < $arr[$right]) { $largest = $right; } if ($largest != $k) { list($arr[$largest], $arr[$k]) = [$arr[$k], $arr[$largest]]; heapify($largest, $heapSize, $arr); } } 堆排序的时间复杂度为O(nlog n),空间复杂度为O(1)。对比归并排序,堆排序有更好的表现。 PHP中的SplHeap、SplMinHeap和SplMaxHeap 当然,方便的PHP内置的标准库已经帮助我实现了堆,你可以通过SplHeap、SplMinHeap、SplMaxHeap来使用它们。
前端项目在服务器a,nuxt2写的,首页使用了ssr,其它页面csr,服务器配置 location / { proxy_pass http://localhost:3000; } 后端api在服务器b,使用laravel开发,其中加入了对服务器a开启的跨域中间件。 现在发现,访问项目时,首页没有渲染,F5刷新也没有,其他页面可以渲染,从其它页面切到首页时首页可以渲染,求解。
namespace app\facade; use app\service\AdminService; class TestFacade { function test() { /** * @var AdminService $adminServcie */ $adminService = invoke(AdminService::class); } } 上面的代码是,我从容器里面实例化的一个类,AdminService,invoke返回的是一个mixed任意类型,这样我就出现了一个问题了。 如果我不写@var 标注,我的编辑器就无法识别这个$adminService 实例下的方法了。 请问如何将$adminService 转换成AdminServcie 类型! $adminService = (AdminService)invoke(AdminService::class); $adminService = (AdminService::class)invoke(AdminService::class); // 这样写是错的
laravel的一个问题,我简化了一下是这样。 我想让访问接口 /a,未登录状态访问返回 0, 登录状态下返回 1,如何实现? 正常是这样。topic 状态为 1 时可以任何人访问,状态未 2 时,只有作者可以访问。 // route.php Route::get('/topics/{topic}', [\App\Http\Controllers\Api\TopicController::class, 'show']) // controller $this->authorize('view', $topic); // TopicPolicy return $topic->status == 1 || $topic->user_id == $user->id; 上边这种情况,无论登录与否,$user 都是 null。 如果 route 加上 middleware('auth'), 那么未登录状态又无法访问。
$router->group(['namespace'=>'\App\Http\Controllers','prefix' => 'api'], function () use ($router) { $router->post('register', 'UserController@register'); $router->post('login', 'UserController@login'); $router->post('info', 'UserController@info'); $router->post('logout', 'UserController@logout'); }); laravel的路由可以使用Route::get('/user', [UserController::class, 'index']);