where('sSlug', config('tutorials.beginner_slug'))->where('iStatus', 1)->first(); $slugs = array_values(array_unique(config('tutorials.pinned_slugs', [config('tutorials.guide_slug')]))); $articles = DB::table('articles')->whereIn('sSlug', $slugs)->where('iStatus', 1)->get(); if (! $category || ! $slugs || $articles->count() !== count($slugs) || $articles->contains(fn ($article) => (int) $article->iCategoryId !== (int) $category->id)) { $this->error('Publish every configured entry article in the beginner category before applying metadata.'); return self::FAILURE; } $this->line(json_encode(['category' => $category->sSlug, 'name' => config('tutorials.beginner_name'), 'articles' => $slugs, 'pinned' => true], JSON_UNESCAPED_UNICODE)); if (! $this->option('apply')) return self::SUCCESS; $backup = storage_path('app/private/editorial/tutorial-metadata-'.Str::uuid().'.json'); $snapshot = ['category_id' => $category->id, 'sName' => $category->sName, 'articles' => $articles->map(fn ($article) => ['id' => $article->id, 'sSlug' => $article->sSlug, 'iIsTop' => $article->iIsTop])->all()]; $json = json_encode($snapshot, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); if (file_put_contents($backup, $json, LOCK_EX) !== strlen($json) || ! chmod($backup, 0600)) { throw new \RuntimeException('Unable to save the private metadata backup.'); } DB::transaction(function () use ($category, $articles) { $currentCategory = DB::table('categories')->where('id', $category->id)->lockForUpdate()->first(); if (! $currentCategory || $currentCategory->sName !== $category->sName || ! $currentCategory->iStatus) { throw new \RuntimeException('Tutorial category changed; inspect before retrying.'); } foreach ($articles as $article) { $current = DB::table('articles')->where('id', $article->id)->lockForUpdate()->first(); if (! $current || (int) $current->iCategoryId !== (int) $category->id || ! $current->iStatus || $current->sSlug !== $article->sSlug || (int) $current->iIsTop !== (int) $article->iIsTop) { throw new \RuntimeException('Tutorial metadata changed; inspect before retrying.'); } DB::table('articles')->where('id', $article->id)->update(['iIsTop' => 1]); } DB::table('categories')->where('id', $category->id)->update(['sName' => config('tutorials.beginner_name')]); }); Cache::forget('global_nav_tree'); \Spatie\ResponseCache\Facades\ResponseCache::clear(); $this->info('Category name saved and configured entry articles pinned. Private backup: '.$backup); return self::SUCCESS; } }