diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e6d0709..160d817 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ jobs: release: uses: saucebase-dev/saucebase/.github/workflows/semantic-release.yml@main with: - version_file: 'module.json' + version_file: 'composer.json' version_file_path: '.version' prerelease: false secrets: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fd84ecd..d29720f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,5 +13,5 @@ jobs: test: uses: saucebase-dev/saucebase/.github/workflows/test-module.yml@main with: - module: Blog + module: blog dependencies: saucebase/auth diff --git a/CLAUDE.md b/CLAUDE.md index bc17df0..7a11c6a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -57,7 +57,7 @@ When adding a field visible on the frontend, update **all three** in sync: php -d memory_limit=2048M artisan test --testsuite=Modules --filter='^Modules\\Blog\\Tests' # E2E -npx playwright test --project="@Blog*" +npx playwright test --project="@blog*" ``` --- diff --git a/LICENSE b/LICENSE index f9a509b..cf78a69 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2025 Saucebase +Copyright (c) 2026 Saucebase Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Taskfile.yml b/Taskfile.yml index f361133..46b1848 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -9,11 +9,17 @@ tasks: test:e2e: desc: Run E2E tests for Blog module - cmd: npx playwright test --project="@Blog*" {{.CLI_ARGS}} + cmd: npx playwright test --project="@blog*" {{.CLI_ARGS}} interactive: true + # ── Database ────────────────────────────────────────────────── + + db:seed: + desc: Seed the Blog module database + cmd: php artisan modules:seed --module=blog + # ── Code Generation ──────────────────────────────────────────── types:generate: desc: Generate TypeScript types from PHP DTOs and enums - cmd: php artisan module:generate-types Blog + cmd: php artisan module:generate-types blog diff --git a/app/Providers/BlogServiceProvider.php b/app/Providers/BlogServiceProvider.php deleted file mode 100644 index 17363c2..0000000 --- a/app/Providers/BlogServiceProvider.php +++ /dev/null @@ -1,16 +0,0 @@ -group(module_path('Blog', '/routes/web.php')); - Route::middleware('api') - ->group(module_path('Blog', '/routes/api.php')); - } -} diff --git a/composer.json b/composer.json index 48e9508..a1641c0 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,9 @@ { "name": "saucebase/blog", - "description": "", + "description": "Blog module", "type": "saucebase-module", + "license": "proprietary", + "version": "0.1.0", "authors": [ { "name": "Saucebase", @@ -10,13 +12,15 @@ ], "extra": { "laravel": { - "providers": [], + "providers": [ + "Modules\\Blog\\Providers\\BlogServiceProvider" + ], "aliases": {} } }, "autoload": { "psr-4": { - "Modules\\Blog\\": "app/", + "Modules\\Blog\\": "src/", "Modules\\Blog\\Database\\Factories\\": "database/factories/", "Modules\\Blog\\Database\\Seeders\\": "database/seeders/" } @@ -25,5 +29,6 @@ "psr-4": { "Modules\\Blog\\Tests\\": "tests/" } - } + }, + "minimum-stability": "stable" } diff --git a/database/seeders/BlogDatabaseSeeder.php b/database/seeders/BlogDatabaseSeeder.php index b565dc8..ee579b2 100644 --- a/database/seeders/BlogDatabaseSeeder.php +++ b/database/seeders/BlogDatabaseSeeder.php @@ -20,7 +20,7 @@ public function run(): void $content = fn (string $file): string => file_get_contents(__DIR__.'/content/'.$file); - Post::create([ + $this->createPost([ 'title' => 'What Is Saucebase? The Modular Laravel Starter Kit', 'excerpt' => 'Every SaaS starts the same way: weeks of setup before you write a single line of real product code. Saucebase is the modular Laravel starter kit that skips that part.', 'content' => $content('post-1-what-is-saucebase.html'), @@ -28,9 +28,9 @@ public function run(): void 'published_at' => now()->subMonths(5), 'category_id' => $gettingStarted->id, 'author_id' => $author?->id, - ]); + ], public_path('images/blog/what-is-saucebase.jpg')); - Post::create([ + $this->createPost([ 'title' => 'Stop Rebuilding the Same Boilerplate Every Project', 'excerpt' => "If you've launched more than one Laravel app, you know the feeling — two weeks gone before you write any real product code. There's a better way.", 'content' => $content('post-2-stop-rebuilding-boilerplate.html'), @@ -40,17 +40,17 @@ public function run(): void 'author_id' => $author?->id, ]); - Post::create([ - 'title' => 'The VILT Stack: Laravel, Vue, Inertia, and Tailwind Done Right', - 'excerpt' => "Modern web dev has a tension between server productivity and rich interactivity. The VILT stack resolves it — and Saucebase wires all four pieces together so you don't have to.", + $this->createPost([ + 'title' => 'The Modern Laravel Stack: Inertia.js, Vue 3 or React, and Tailwind Done Right', + 'excerpt' => "Modern web dev has a tension between server productivity and rich interactivity. Laravel, Inertia.js, and Tailwind resolve it — and Saucebase wires all the pieces together so you don't have to.", 'content' => $content('post-3-vilt-stack.html'), 'status' => PostStatus::Published, 'published_at' => now()->subMonths(3), 'category_id' => $devExperience->id, 'author_id' => $author?->id, - ]); + ], public_path('images/blog/tech-stack.jpg')); - $post4 = Post::create([ + $this->createPost([ 'title' => 'Your First Module: Scaffold and Ship in Under 10 Minutes', 'excerpt' => "One Artisan command scaffolds a fully structured module — service provider, controller, routes, Vue pages, migrations, factory, tests, and Vite config. Here's how it works.", 'content' => $content('post-4-your-first-module.html'), @@ -58,14 +58,9 @@ public function run(): void 'published_at' => now()->subMonths(2), 'category_id' => $featuresModules->id, 'author_id' => $author?->id, - ]); + ], public_path('images/blog/add-your-saucebase.jpg')); - $image4 = public_path('images/blog/add-your-saucebase.jpg'); - if (file_exists($image4)) { - $post4->addMedia($image4)->preservingOriginal()->toMediaCollection('cover'); - } - - $post5 = Post::create([ + $this->createPost([ 'title' => 'Auth, Billing, and Privacy: The Three Modules Every SaaS Needs', 'excerpt' => "Three modules every SaaS needs — and Saucebase ships them already built. Here's what's included and why they matter.", 'content' => $content('post-5-auth-billing-privacy.html'), @@ -73,14 +68,9 @@ public function run(): void 'published_at' => now()->subMonth(), 'category_id' => $featuresModules->id, 'author_id' => $author?->id, - ]); + ], public_path('images/blog/cookies-or-privacy.jpg')); - $image5 = public_path('images/blog/cookies-or-privacy.jpg'); - if (file_exists($image5)) { - $post5->addMedia($image5)->preservingOriginal()->toMediaCollection('cover'); - } - - $post6 = Post::create([ + $this->createPost([ 'title' => 'Copy-and-Own: The Philosophy Behind Saucebase Modules', 'excerpt' => "Most starter kits give you a locked box. Saucebase takes the opposite approach: when you install a module, the source code lands in your repo and it's yours to read, edit, and own.", 'content' => $content('post-6-copy-and-own.html'), @@ -88,11 +78,17 @@ public function run(): void 'published_at' => now()->subWeeks(2), 'category_id' => $devExperience->id, 'author_id' => $author?->id, - ]); + ], public_path('images/blog/your-recipes.jpg')); + } - $image6 = public_path('images/blog/your-recipes.jpg'); - if (file_exists($image6)) { - $post6->addMedia($image6)->preservingOriginal()->toMediaCollection('cover'); + private function createPost(array $data, string $coverImage = ''): Post + { + $post = Post::create($data); + + if ($coverImage && file_exists($coverImage)) { + $post->addMedia($coverImage)->preservingOriginal()->toMediaCollection('cover'); } + + return $post; } } diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php new file mode 100644 index 0000000..f29ab7b --- /dev/null +++ b/database/seeders/DatabaseSeeder.php @@ -0,0 +1,13 @@ +call(BlogDatabaseSeeder::class); + } +} diff --git a/database/seeders/content/post-1-what-is-saucebase.html b/database/seeders/content/post-1-what-is-saucebase.html index caa1a7e..2680d10 100644 --- a/database/seeders/content/post-1-what-is-saucebase.html +++ b/database/seeders/content/post-1-what-is-saucebase.html @@ -2,9 +2,9 @@

Saucebase exists to solve that problem.

-

Saucebase is a modular Laravel SaaS starter kit built on the VILT stack: Vue 3, Inertia.js, Laravel 13, and Tailwind CSS 4. It ships with the infrastructure every SaaS needs and gets it out of your way so you can focus on building what makes your product unique.

+

Saucebase is a modular Laravel SaaS starter kit built on Laravel 13, Inertia.js, and Tailwind CSS 4 — with your choice of Vue 3 or React. It ships with the infrastructure every SaaS needs and gets it out of your way so you can focus on building what makes your product unique.

-

The core of Saucebase is its module system, powered by nwidart/laravel-modules. Features are packaged as self-contained modules — Auth, Billing, Settings, Blog — each with their own routes, controllers, models, migrations, and Vue pages. Enable what you need, disable what you don't.

+

The core of Saucebase is its module system, powered by internachi/modular. Features are packaged as self-contained modules — Auth, Billing, Settings, Blog — each with their own routes, controllers, models, migrations, and frontend pages. Modules are active when installed via Composer and live directly in your repository.

But here's what sets Saucebase apart: modules are copy-and-own. When you install a module, you own it. It lives in your repository. You can edit it, extend it, break it, and fix it. There are no locked vendor packages to work around, no version conflicts to manage. Just clean, readable Laravel code that does exactly what you'd write yourself — except already written.

diff --git a/database/seeders/content/post-3-vilt-stack.html b/database/seeders/content/post-3-vilt-stack.html index 3c91f42..ce98521 100644 --- a/database/seeders/content/post-3-vilt-stack.html +++ b/database/seeders/content/post-3-vilt-stack.html @@ -1,4 +1,4 @@ -

Modern web development has a tension at its core: server-side frameworks are productive but struggle with rich interactivity, while JavaScript SPAs offer great UX but introduce enormous complexity. The VILT stack resolves that tension elegantly — and Saucebase is built on it from the ground up.

+

Modern web development has a tension at its core: server-side frameworks are productive but struggle with rich interactivity, while JavaScript SPAs offer great UX but introduce enormous complexity. The Laravel + Inertia.js stack resolves that tension elegantly — and Saucebase is built on it from the ground up.

Laravel — the foundation

@@ -6,13 +6,13 @@

Laravel — the foundation

Inertia.js — the bridge

-

Version 3 replaces traditional Blade views with Vue components while keeping your server-side routing and controllers intact. No REST API, no GraphQL, no data-fetching layer to maintain. Your controller returns a component name and props. Inertia handles the rest.

+

Version 3 replaces traditional Blade views with Vue or React components while keeping your server-side routing and controllers intact. No REST API, no GraphQL, no data-fetching layer to maintain. Your controller returns a component name and props. Inertia handles the rest.

Version 3 brings deferred props, optimistic updates with automatic rollback, instant visits, and simplified SSR via the Vite plugin — all of which Saucebase uses by default.

-

Vue 3 — the UI layer

+

Vue 3 or React — the UI layer

-

Composition API, <script setup>, and TypeScript throughout. Pages live in resources/js/pages/, components in resources/js/components/. Types are auto-generated from your PHP enums and DTOs via php artisan module:generate-types — your backend and frontend stay in sync without manual work.

+

Saucebase ships with full support for both frameworks. Pick Vue 3 (Composition API, <script setup>) or React 19 (hooks, JSX) — the choice is made during setup and everything is wired up for you. TypeScript is used throughout either way, and types are auto-generated from your PHP enums and DTOs via php artisan module:generate-types — your backend and frontend stay in sync without manual work.

Tailwind CSS 4 — the styling layer

@@ -21,7 +21,7 @@

Tailwind CSS 4 — the styling layer

The full picture

diff --git a/database/seeders/content/post-5-auth-billing-privacy.html b/database/seeders/content/post-5-auth-billing-privacy.html index f4dc440..cb3747a 100644 --- a/database/seeders/content/post-5-auth-billing-privacy.html +++ b/database/seeders/content/post-5-auth-billing-privacy.html @@ -8,7 +8,6 @@

Auth module

  • Registration, login, and logout
  • Email verification and password reset
  • Social login via Laravel Socialite
  • -
  • Two-factor authentication
  • Role-based access with Spatie Laravel Permission (admin and user seeded by default)
  • @@ -33,7 +32,6 @@

    Settings module

  • Profile editing and avatar upload
  • Email change with re-verification
  • Password management
  • -
  • Notification preferences
  • Privacy and compliance

    diff --git a/database/seeders/content/post-6-copy-and-own.html b/database/seeders/content/post-6-copy-and-own.html index aad6e08..5cd48d2 100644 --- a/database/seeders/content/post-6-copy-and-own.html +++ b/database/seeders/content/post-6-copy-and-own.html @@ -10,7 +10,7 @@

    What copy-and-own means

    The practical benefit

    -

    When a bug appears in your authentication flow, you look at modules/Auth/app/Http/Controllers/. When you want to add a field to the billing portal, you edit modules/Billing/resources/js/pages/. There's no mental overhead of figuring out which hook to use or which config key controls the behaviour you need. You find the code and change it.

    +

    When a bug appears in your authentication flow, you look at modules/auth/app/Http/Controllers/. When you want to add a field to the billing portal, you edit modules/billing/resources/js/pages/. There's no mental overhead of figuring out which hook to use or which config key controls the behaviour you need. You find the code and change it.

    The tradeoff

    diff --git a/module.json b/module.json deleted file mode 100644 index 33ba675..0000000 --- a/module.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "Blog", - "alias": "blog", - "description": "Blog module", - "author": "Saucebase", - "version": "v0.1.0", - "keywords": [ - "blog" - ], - "priority": 0, - "providers": [ - "Modules\\Blog\\Providers\\BlogServiceProvider" - ], - "files": [] -} diff --git a/resources/js/app.ts b/resources/js/app.ts index 3aa2b51..3043f15 100644 --- a/resources/js/app.ts +++ b/resources/js/app.ts @@ -1,11 +1 @@ -import { registerIcon } from '@/lib/navigation'; - -import '../css/style.css'; - -import IconBlog from '~icons/heroicons/newspaper'; - -export function setup() { - registerIcon('blog', IconBlog); -} - -export function afterMount() {} +export * from './vue/app'; diff --git a/resources/js/types/page-props.d.ts b/resources/js/types/page-props.d.ts index acfca86..01e84f4 100644 --- a/resources/js/types/page-props.d.ts +++ b/resources/js/types/page-props.d.ts @@ -2,7 +2,7 @@ // Augmentations here are auto-picked up when the module is enabled (no core edits needed). // See docs: architecture/module-system#typescript-page-props -// declare module '@inertiajs/vue3' { +// declare module '@inertiajs/core' { // interface PageProps { // // my_shared_prop?: MyType; // } diff --git a/resources/js/vue/app.ts b/resources/js/vue/app.ts new file mode 100644 index 0000000..a55c0ff --- /dev/null +++ b/resources/js/vue/app.ts @@ -0,0 +1,11 @@ +import { registerIcon } from '@/lib/navigation'; + +import '@modules/blog/resources/css/style.css'; + +import IconBlog from '~icons/heroicons/newspaper'; + +export function setup() { + registerIcon('blog', IconBlog); +} + +export function afterMount() {} diff --git a/resources/js/components/LatestPostsSection.vue b/resources/js/vue/components/LatestPostsSection.vue similarity index 88% rename from resources/js/components/LatestPostsSection.vue rename to resources/js/vue/components/LatestPostsSection.vue index d158573..2140ca9 100644 --- a/resources/js/components/LatestPostsSection.vue +++ b/resources/js/vue/components/LatestPostsSection.vue @@ -11,7 +11,9 @@ defineProps<{
    -

    +

    {{ $t('From the blog') }}

    diff --git a/resources/js/components/PostCard.vue b/resources/js/vue/components/PostCard.vue similarity index 97% rename from resources/js/components/PostCard.vue rename to resources/js/vue/components/PostCard.vue index 82e441b..9a32542 100644 --- a/resources/js/components/PostCard.vue +++ b/resources/js/vue/components/PostCard.vue @@ -33,7 +33,7 @@ defineProps<{

    -
    +
    {{ author.name.charAt(0).toUpperCase() }}
    - {{ author.name }} - + {{ + author.name + }} +
    diff --git a/resources/js/pages/Index.vue b/resources/js/vue/pages/Index.vue similarity index 78% rename from resources/js/pages/Index.vue rename to resources/js/vue/pages/Index.vue index af62fd6..72f27d6 100644 --- a/resources/js/pages/Index.vue +++ b/resources/js/vue/pages/Index.vue @@ -44,7 +44,11 @@ defineProps<{ v-else class="grid grid-cols-1 gap-8 sm:grid-cols-1 lg:grid-cols-2" > - +
    @@ -55,14 +59,14 @@ defineProps<{ {{ $t('← Previous') }} {{ $t('Next →') }} diff --git a/resources/js/pages/Show.vue b/resources/js/vue/pages/Show.vue similarity index 74% rename from resources/js/pages/Show.vue rename to resources/js/vue/pages/Show.vue index 5298865..13b32c3 100644 --- a/resources/js/pages/Show.vue +++ b/resources/js/vue/pages/Show.vue @@ -33,10 +33,22 @@ const jsonLd = computed(() => ({ type="article" > - - + + - +
    @@ -57,7 +69,10 @@ const jsonLd = computed(() => ({
    - +
    @@ -82,15 +97,21 @@ const jsonLd = computed(() => ({
    -
    +

    {{ $t('You might also like') }}

    -
    - +
    +
    diff --git a/routes/api.php b/routes/api.php index 899a436..beebb37 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,3 +1,5 @@ group(function (): void { + // No API routes for Blog module +}); diff --git a/routes/navigation.php b/routes/navigation.php index 0a29566..c0d1f95 100644 --- a/routes/navigation.php +++ b/routes/navigation.php @@ -13,7 +13,7 @@ | */ -Navigation::add('Blog', route('blog.index'), function (Section $section) { +Navigation::add('Blog', fn () => route('blog.index'), function (Section $section) { $section->attributes([ 'group' => 'landing', 'slug' => 'blog', diff --git a/routes/web.php b/routes/web.php index a23cc85..223c09b 100644 --- a/routes/web.php +++ b/routes/web.php @@ -3,6 +3,8 @@ use Illuminate\Support\Facades\Route; use Modules\Blog\Http\Controllers\BlogController; -Route::get('/blog', [BlogController::class, 'index'])->name('blog.index'); -Route::get('/blog/{category}/{slug}', [BlogController::class, 'show'])->name('blog.show.category'); -Route::get('/blog/{slug}', [BlogController::class, 'show'])->name('blog.show'); +Route::middleware('web')->group(function (): void { + Route::get('/blog', [BlogController::class, 'index'])->name('blog.index'); + Route::get('/blog/{category}/{slug}', [BlogController::class, 'show'])->name('blog.show.category'); + Route::get('/blog/{slug}', [BlogController::class, 'show'])->name('blog.show'); +}); diff --git a/app/Data/AuthorData.php b/src/Data/AuthorData.php similarity index 100% rename from app/Data/AuthorData.php rename to src/Data/AuthorData.php diff --git a/app/Data/CategoryData.php b/src/Data/CategoryData.php similarity index 100% rename from app/Data/CategoryData.php rename to src/Data/CategoryData.php diff --git a/app/Data/PostData.php b/src/Data/PostData.php similarity index 100% rename from app/Data/PostData.php rename to src/Data/PostData.php diff --git a/app/Enums/PostStatus.php b/src/Enums/PostStatus.php similarity index 100% rename from app/Enums/PostStatus.php rename to src/Enums/PostStatus.php diff --git a/app/Filament/BlogPlugin.php b/src/Filament/BlogPlugin.php similarity index 100% rename from app/Filament/BlogPlugin.php rename to src/Filament/BlogPlugin.php diff --git a/app/Filament/Resources/Blog/CategoryResource.php b/src/Filament/Resources/Blog/CategoryResource.php similarity index 100% rename from app/Filament/Resources/Blog/CategoryResource.php rename to src/Filament/Resources/Blog/CategoryResource.php diff --git a/app/Filament/Resources/Blog/Pages/CreateCategory.php b/src/Filament/Resources/Blog/Pages/CreateCategory.php similarity index 100% rename from app/Filament/Resources/Blog/Pages/CreateCategory.php rename to src/Filament/Resources/Blog/Pages/CreateCategory.php diff --git a/app/Filament/Resources/Blog/Pages/CreatePost.php b/src/Filament/Resources/Blog/Pages/CreatePost.php similarity index 100% rename from app/Filament/Resources/Blog/Pages/CreatePost.php rename to src/Filament/Resources/Blog/Pages/CreatePost.php diff --git a/app/Filament/Resources/Blog/Pages/EditCategory.php b/src/Filament/Resources/Blog/Pages/EditCategory.php similarity index 100% rename from app/Filament/Resources/Blog/Pages/EditCategory.php rename to src/Filament/Resources/Blog/Pages/EditCategory.php diff --git a/app/Filament/Resources/Blog/Pages/EditPost.php b/src/Filament/Resources/Blog/Pages/EditPost.php similarity index 100% rename from app/Filament/Resources/Blog/Pages/EditPost.php rename to src/Filament/Resources/Blog/Pages/EditPost.php diff --git a/app/Filament/Resources/Blog/Pages/ListCategories.php b/src/Filament/Resources/Blog/Pages/ListCategories.php similarity index 100% rename from app/Filament/Resources/Blog/Pages/ListCategories.php rename to src/Filament/Resources/Blog/Pages/ListCategories.php diff --git a/app/Filament/Resources/Blog/Pages/ListPosts.php b/src/Filament/Resources/Blog/Pages/ListPosts.php similarity index 100% rename from app/Filament/Resources/Blog/Pages/ListPosts.php rename to src/Filament/Resources/Blog/Pages/ListPosts.php diff --git a/app/Filament/Resources/Blog/PostResource.php b/src/Filament/Resources/Blog/PostResource.php similarity index 100% rename from app/Filament/Resources/Blog/PostResource.php rename to src/Filament/Resources/Blog/PostResource.php diff --git a/app/Filament/Resources/Blog/Schemas/CategoryForm.php b/src/Filament/Resources/Blog/Schemas/CategoryForm.php similarity index 100% rename from app/Filament/Resources/Blog/Schemas/CategoryForm.php rename to src/Filament/Resources/Blog/Schemas/CategoryForm.php diff --git a/app/Filament/Resources/Blog/Schemas/PostForm.php b/src/Filament/Resources/Blog/Schemas/PostForm.php similarity index 100% rename from app/Filament/Resources/Blog/Schemas/PostForm.php rename to src/Filament/Resources/Blog/Schemas/PostForm.php diff --git a/app/Filament/Resources/Blog/Tables/CategoriesTable.php b/src/Filament/Resources/Blog/Tables/CategoriesTable.php similarity index 100% rename from app/Filament/Resources/Blog/Tables/CategoriesTable.php rename to src/Filament/Resources/Blog/Tables/CategoriesTable.php diff --git a/app/Filament/Resources/Blog/Tables/PostsTable.php b/src/Filament/Resources/Blog/Tables/PostsTable.php similarity index 100% rename from app/Filament/Resources/Blog/Tables/PostsTable.php rename to src/Filament/Resources/Blog/Tables/PostsTable.php diff --git a/app/Http/Controllers/BlogController.php b/src/Http/Controllers/BlogController.php similarity index 100% rename from app/Http/Controllers/BlogController.php rename to src/Http/Controllers/BlogController.php diff --git a/app/Models/Category.php b/src/Models/Category.php similarity index 100% rename from app/Models/Category.php rename to src/Models/Category.php diff --git a/app/Models/Post.php b/src/Models/Post.php similarity index 100% rename from app/Models/Post.php rename to src/Models/Post.php diff --git a/src/Providers/BlogServiceProvider.php b/src/Providers/BlogServiceProvider.php new file mode 100644 index 0000000..c73e37a --- /dev/null +++ b/src/Providers/BlogServiceProvider.php @@ -0,0 +1,9 @@ +