Laravel File Storage: Uploading and Managing Files with the Storage Facade
Handle file uploads and storage across different drivers.
Database seeding is the process of populating your database with sample or test data. This is essential for:
# Create a new seeder
php artisan make:seeder UserSeeder
# Create multiple seeders
php artisan make:seeder PostSeeder
php artisan make:seeder CommentSeeder
<?php
// database/seeders/UserSeeder.php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use App\Models\User;
class UserSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
// Basic user creation
User::create([
'name' => 'John Doe',
'email' => 'john@teachyleaf.com',
'password' => bcrypt('password123'),
]);
// Multiple users
User::create([
'name' => 'Jane Smith',
'email' => 'jane@teachyleaf.com',
'password' => bcrypt('password123'),
]);
User::create([
'name' => 'Admin User',
'email' => 'admin@teachyleaf.com',
'password' => bcrypt('password123'),
'is_admin' => true,
]);
}
}
# Run all seeders
php artisan db:seed
# Run specific seeder
php artisan db:seed --class=UserSeeder
# Refresh database and seed
php artisan migrate:refresh --seed
# Fresh migration and seed
php artisan migrate:fresh --seed
# Create factory for User model
php artisan make:factory UserFactory
# Create factory for specific model
php artisan make:factory PostFactory --model=Post
# Create multiple factories
php artisan make:factory CategoryFactory --model=Category
php artisan make:factory TagFactory --model=Tag
<?php
// database/factories/UserFactory.php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
*/
class UserFactory extends Factory
{
/**
* The current password being used by the factory.
*/
protected static ?string $password;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => static::$password ??= Hash::make('password'),
'remember_token' => Str::random(10),
];
}
/**
* Indicate that the model's email address should be unverified.
*/
public function unverified(): static
{
return $this->state(fn (array $attributes) => [
'email_verified_at' => null,
]);
}
}
// Names and Contact
'name' => fake()->name(),
'first_name' => fake()->firstName(),
'last_name' => fake()->lastName(),
'email' => fake()->unique()->safeEmail(),
'phone' => fake()->phoneNumber(),
// Address
'address' => fake()->address(),
'city' => fake()->city(),
'state' => fake()->state(),
'country' => fake()->country(),
'zip_code' => fake()->postcode(),
// Text Content
'title' => fake()->sentence(),
'sentence' => fake()->sentence(6),
'paragraph' => fake()->paragraph(3),
'text' => fake()->text(200),
// Numbers and Dates
'number' => fake()->randomNumber(2),
'price' => fake()->randomFloat(2, 10, 100),
'age' => fake()->numberBetween(18, 65),
'birth_date' => fake()->date(),
// Internet
'username' => fake()->userName(),
'domain' => fake()->domainName(),
'url' => fake()->url(),
'slug' => fake()->slug(),
// Business
'company' => fake()->company(),
'job_title' => fake()->jobTitle(),
// Images and Files
'image_url' => fake()->imageUrl(640, 480, 'nature', true),
// Boolean and Enums
'is_active' => fake()->boolean(),
'status' => fake()->randomElement(['active', 'inactive', 'pending']),
<?php
// database/factories/UserFactory.php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
class UserFactory extends Factory
{
protected static ?string $password;
public function definition(): array
{
return [
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'username' => fake()->unique()->userName(),
'phone' => fake()->phoneNumber(),
'bio' => fake()->paragraph(2),
'avatar' => fake()->imageUrl(100, 100, 'people', true),
'website' => fake()->url(),
'location' => fake()->city() . ', ' . fake()->country(),
'email_verified_at' => now(),
'password' => static::$password ??= Hash::make('password'),
'remember_token' => Str::random(10),
'created_at' => fake()->dateTimeBetween('-1 year', 'now'),
'updated_at' => fake()->dateTimeBetween('-1 year', 'now'),
];
}
public function unverified(): static
{
return $this->state(fn (array $attributes) => [
'email_verified_at' => null,
]);
}
public function admin(): static
{
return $this->state(fn (array $attributes) => [
'is_admin' => true,
'role' => 'admin',
]);
}
public function moderator(): static
{
return $this->state(fn (array $attributes) => [
'role' => 'moderator',
]);
}
public function withSpecificName(string $name): static
{
return $this->state(fn (array $attributes) => [
'name' => $name,
]);
}
public function inactive(): static
{
return $this->state(fn (array $attributes) => [
'is_active' => false,
]);
}
public function suspended(): static
{
return $this->state(fn (array $attributes) => [
'suspended_at' => now(),
'suspension_reason' => fake()->sentence(),
]);
}
}
<?php
// database/factories/PostFactory.php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
class PostFactory extends Factory
{
public function definition(): array
{
$title = fake()->sentence(6);
return [
'title' => $title,
'slug' => Str::slug($title),
'excerpt' => fake()->paragraph(2),
'content' => $this->generateContent(),
'featured_image' => fake()->imageUrl(800, 400, 'nature', true),
'meta_title' => fake()->sentence(),
'meta_description' => fake()->paragraph(),
'is_published' => fake()->boolean(80), // 80% chance of being published
'is_featured' => fake()->boolean(20), // 20% chance of being featured
'published_at' => fake()->dateTimeBetween('-1 year', '+1 month'),
'views' => fake()->numberBetween(0, 10000),
'reading_time' => fake()->numberBetween(1, 20),
'user_id' => \App\Models\User::factory(),
'created_at' => fake()->dateTimeBetween('-1 year', 'now'),
'updated_at' => fake()->dateTimeBetween('-1 year', 'now'),
];
}
private function generateContent(): string
{
$paragraphs = fake()->paragraphs(rand(5, 15));
$content = '';
foreach ($paragraphs as $paragraph) {
$content .= "<p>{$paragraph}</p>\n\n";
// Occasionally add headings
if (rand(1, 5) === 1) {
$content .= "<h2>" . fake()->sentence(4) . "</h2>\n\n";
}
// Occasionally add blockquotes
if (rand(1, 8) === 1) {
$content .= "<blockquote>" . fake()->sentence(10) . "</blockquote>\n\n";
}
}
return $content;
}
public function published(): static
{
return $this->state(fn (array $attributes) => [
'is_published' => true,
'published_at' => fake()->dateTimeBetween('-1 year', 'now'),
]);
}
public function draft(): static
{
return $this->state(fn (array $attributes) => [
'is_published' => false,
'published_at' => null,
]);
}
public function featured(): static
{
return $this->state(fn (array $attributes) => [
'is_featured' => true,
]);
}
public function withUser(int $userId): static
{
return $this->state(fn (array $attributes) => [
'user_id' => $userId,
]);
}
public function withSpecificTitle(string $title): static
{
return $this->state(fn (array $attributes) => [
'title' => $title,
'slug' => Str::slug($title),
]);
}
}
<?php
// database/factories/CommentFactory.php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
class CommentFactory extends Factory
{
public function definition(): array
{
// Randomly decide if this is for a Post or Video
$commentableType = fake()->randomElement([
\App\Models\Post::class,
\App\Models\Video::class,
]);
return [
'body' => fake()->paragraph(rand(1, 4)),
'user_id' => \App\Models\User::factory(),
'commentable_type' => $commentableType,
'commentable_id' => $commentableType::factory(),
'is_approved' => fake()->boolean(85), // 85% approved
'upvotes' => fake()->numberBetween(0, 100),
'downvotes' => fake()->numberBetween(0, 50),
'created_at' => fake()->dateTimeBetween('-1 year', 'now'),
'updated_at' => fake()->dateTimeBetween('-1 year', 'now'),
];
}
public function approved(): static
{
return $this->state(fn (array $attributes) => [
'is_approved' => true,
]);
}
public function pending(): static
{
return $this->state(fn (array $attributes) => [
'is_approved' => false,
]);
}
public function forPost($post = null): static
{
$post = $post ?? \App\Models\Post::factory();
return $this->state(fn (array $attributes) => [
'commentable_type' => \App\Models\Post::class,
'commentable_id' => $post,
]);
}
public function forVideo($video = null): static
{
$video = $video ?? \App\Models\Video::factory();
return $this->state(fn (array $attributes) => [
'commentable_type' => \App\Models\Video::class,
'commentable_id' => $video,
]);
}
public function withUser($user): static
{
return $this->state(fn (array $attributes) => [
'user_id' => $user,
]);
}
}
<?php
// database/seeders/DatabaseSeeder.php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
class DatabaseSeeder extends Seeder
{
public function run(): void
{
// Disable foreign key checks for better performance
DB::statement('SET FOREIGN_KEY_CHECKS=0;');
// Clear existing data
$this->call(ClearDataSeeder::class);
// Seed in correct order to maintain foreign key relationships
$this->call([
UserSeeder::class,
CategorySeeder::class,
TagSeeder::class,
PostSeeder::class,
CommentSeeder::class,
PostTagSeeder::class,
]);
// Enable foreign key checks
DB::statement('SET FOREIGN_KEY_CHECKS=1;');
}
}
<?php
// database/seeders/UserSeeder.php
namespace Database\Seeders;
use App\Models\User;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Hash;
class UserSeeder extends Seeder
{
public function run(): void
{
// Create specific test users
$admin = User::create([
'name' => 'Admin User',
'email' => 'admin@teachyleaf.com',
'password' => Hash::make('password123'),
'is_admin' => true,
'email_verified_at' => now(),
]);
$author = User::create([
'name' => 'Content Author',
'email' => 'author@teachyleaf.com',
'password' => Hash::make('password123'),
'email_verified_at' => now(),
]);
$moderator = User::create([
'name' => 'Content Moderator',
'email' => 'moderator@teachyleaf.com',
'password' => Hash::make('password123'),
'role' => 'moderator',
'email_verified_at' => now(),
]);
// Create random users using factories
User::factory()->count(50)->create();
// Create users with specific states
User::factory()->count(5)->admin()->create();
User::factory()->count(3)->moderator()->create();
User::factory()->count(2)->unverified()->create();
User::factory()->count(2)->inactive()->create();
User::factory()->count(1)->suspended()->create();
$this->command->info('Users seeded successfully!');
$this->command->info("Admin: admin@teachyleaf.com / password123");
$this->command->info("Author: author@teachyleaf.com / password123");
}
}
<?php
// database/seeders/PostSeeder.php
namespace Database\Seeders;
use App\Models\Post;
use App\Models\User;
use App\Models\Category;
use App\Models\Tag;
use Illuminate\Database\Seeder;
class PostSeeder extends Seeder
{
public function run(): void
{
// Get all users and categories for relationship assignment
$users = User::all();
$categories = Category::all();
$tags = Tag::all();
// Create specific featured posts
$featuredPosts = [
[
'title' => 'Getting Started with Laravel',
'content' => $this->generateContent('Laravel'),
'user_id' => $users->where('email', 'author@teachyleaf.com')->first()->id,
'is_featured' => true,
'is_published' => true,
],
[
'title' => 'Advanced Eloquent Relationships',
'content' => $this->generateContent('Eloquent'),
'user_id' => $users->where('email', 'author@teachyleaf.com')->first()->id,
'is_featured' => true,
'is_published' => true,
],
];
foreach ($featuredPosts as $postData) {
$post = Post::create(array_merge($postData, [
'slug' => \Illuminate\Support\Str::slug($postData['title']),
'excerpt' => \Illuminate\Support\Str::limit(strip_tags($postData['content']), 150),
'published_at' => now(),
'category_id' => $categories->random()->id,
]));
// Attach random tags
$post->tags()->attach(
$tags->random(rand(2, 5))->pluck('id')->toArray()
);
}
// Create random posts using factories
$posts = Post::factory()
->count(100)
->create([
'user_id' => fn() => $users->random()->id,
'category_id' => fn() => $categories->random()->id,
]);
// Attach tags to all posts
foreach ($posts as $post) {
$post->tags()->attach(
$tags->random(rand(1, 4))->pluck('id')->toArray()
);
}
// Create some draft posts
Post::factory()
->count(15)
->draft()
->create([
'user_id' => fn() => $users->random()->id,
'category_id' => fn() => $categories->random()->id,
]);
$this->command->info('Posts seeded successfully!');
}
private function generateContent(string $topic): string
{
$paragraphs = [
"This is a comprehensive guide about {$topic}. In this article, we'll explore the fundamental concepts and advanced techniques.",
"Understanding {$topic} is crucial for modern web development. It provides powerful features that make development faster and more efficient.",
"Many developers find {$topic} intimidating at first, but with proper guidance, it becomes an invaluable tool in your development toolkit.",
"Let's dive deeper into the practical applications of {$topic}. We'll look at real-world examples and best practices.",
"One of the key benefits of {$topic} is its flexibility. You can adapt it to various use cases and project requirements.",
"As we continue our exploration of {$topic}, remember that practice is essential. Try out the examples and experiment with your own code.",
"In conclusion, mastering {$topic} will significantly improve your development workflow and application performance."
];
$content = '';
foreach ($paragraphs as $index => $paragraph) {
if ($index % 2 === 0 && $index > 0) {
$content .= "<h3>" . fake()->sentence(4) . "</h3>\n\n";
}
$content .= "<p>{$paragraph}</p>\n\n";
}
return $content;
}
}
<?php
// database/seeders/CommentSeeder.php
namespace Database\Seeders;
use App\Models\Comment;
use App\Models\Post;
use App\Models\Video;
use App\Models\User;
use Illuminate\Database\Seeder;
class CommentSeeder extends Seeder
{
public function run(): void
{
$users = User::all();
$posts = Post::published()->get();
$videos = Video::all();
// Create comments for posts
foreach ($posts as $post) {
$commentCount = rand(0, 15);
Comment::factory()
->count($commentCount)
->forPost($post)
->create([
'user_id' => fn() => $users->random()->id,
]);
}
// Create comments for videos
foreach ($videos as $video) {
$commentCount = rand(0, 10);
Comment::factory()
->count($commentCount)
->forVideo($video)
->create([
'user_id' => fn() => $users->random()->id,
]);
}
// Create some nested comments (replies)
$comments = Comment::all();
foreach ($comments->random(20) as $parentComment) {
Comment::factory()
->count(rand(1, 3))
->create([
'user_id' => fn() => $users->random()->id,
'parent_id' => $parentComment->id,
'commentable_type' => $parentComment->commentable_type,
'commentable_id' => $parentComment->commentable_id,
]);
}
$this->command->info('Comments seeded successfully!');
}
}
<?php
// tests/Feature/PostTest.php
namespace Tests\Feature;
use App\Models\Post;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class PostTest extends TestCase
{
use RefreshDatabase;
public function test_user_can_create_post(): void
{
// Create a user using factory
$user = User::factory()->create();
// Create post data
$postData = [
'title' => 'Test Post',
'content' => 'This is a test post content.',
];
// Act as the user and create post
$response = $this->actingAs($user)
->post('/posts', $postData);
// Assertions
$response->assertRedirect('/posts');
$this->assertDatabaseHas('posts', [
'title' => 'Test Post',
'user_id' => $user->id,
]);
}
public function test_published_posts_are_visible(): void
{
// Create published and draft posts
$publishedPost = Post::factory()->published()->create();
$draftPost = Post::factory()->draft()->create();
// Access posts index
$response = $this->get('/posts');
// Assert only published posts are visible
$response->assertSee($publishedPost->title);
$response->assertDontSee($draftPost->title);
}
public function test_post_with_comments(): void
{
// Create a post with 5 comments
$post = Post::factory()
->hasComments(5)
->create();
$this->assertCount(5, $post->comments);
}
public function test_user_with_posts_and_comments(): void
{
// Create user with 3 posts, each with 2 comments
$user = User::factory()
->hasPosts(3, function (array $attributes, User $user) {
return ['user_id' => $user->id];
})
->hasComments(2)
->create();
$this->assertCount(3, $user->posts);
$this->assertCount(2, $user->comments);
}
}
<?php
// tests/Feature/AdvancedPostTest.php
namespace Tests\Feature;
use App\Models\Post;
use App\Models\User;
use App\Models\Tag;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class AdvancedPostTest extends TestCase
{
use RefreshDatabase;
public function test_post_with_specific_tags(): void
{
// Create tags
$laravelTag = Tag::factory()->create(['name' => 'Laravel']);
$phpTag = Tag::factory()->create(['name' => 'PHP']);
// Create post with specific tags
$post = Post::factory()
->hasAttached([$laravelTag, $phpTag])
->create();
$this->assertCount(2, $post->tags);
$this->assertTrue($post->tags->contains('name', 'Laravel'));
}
public function test_multiple_users_with_different_roles(): void
{
// Create users with different roles
$admin = User::factory()->admin()->create();
$moderator = User::factory()->moderator()->create();
$regularUser = User::factory()->create();
// Create posts for each user
$adminPost = Post::factory()->create(['user_id' => $admin->id]);
$moderatorPost = Post::factory()->create(['user_id' => $moderator->id]);
$userPost = Post::factory()->create(['user_id' => $regularUser->id]);
// Test access control
$this->actingAs($admin)
->get('/admin/posts')
->assertStatus(200);
$this->actingAs($regularUser)
->get('/admin/posts')
->assertStatus(403);
}
public function test_factory_states_combination(): void
{
// Create a featured, published post from last week
$post = Post::factory()
->published()
->featured()
->create([
'published_at' => now()->subWeek(),
]);
$this->assertTrue($post->is_published);
$this->assertTrue($post->is_featured);
$this->assertTrue($post->published_at->lt(now()));
}
}
<?php
// database/seeders/PerformancePostSeeder.php
namespace Database\Seeders;
use App\Models\Post;
use App\Models\User;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class PerformancePostSeeder extends Seeder
{
public function run(): void
{
$users = User::all();
$batchSize = 1000;
$totalPosts = 10000;
$this->command->info("Seeding {$totalPosts} posts...");
$this->command->getOutput()->progressStart($totalPosts);
for ($i = 0; $i < $totalPosts; $i += $batchSize) {
$posts = [];
for ($j = 0; $j < $batchSize && ($i + $j) < $totalPosts; $j++) {
$title = fake()->sentence(6);
$posts[] = [
'title' => $title,
'slug' => \Illuminate\Support\Str::slug($title),
'excerpt' => fake()->paragraph(2),
'content' => fake()->paragraphs(5, true),
'user_id' => $users->random()->id,
'is_published' => fake()->boolean(80),
'published_at' => fake()->dateTimeBetween('-1 year', 'now'),
'created_at' => now(),
'updated_at' => now(),
];
$this->command->getOutput()->progressAdvance();
}
// Use insert for better performance (bypasses model events)
DB::table('posts')->insert($posts);
}
$this->command->getOutput()->progressFinish();
$this->command->info("Seeded {$totalPosts} posts successfully!");
}
}
Seeders are used to populate the database with specific data, while factories define how to generate fake data for models. Factories are more flexible and are often used within seeders.
Use closure callbacks in factory definitions: 'user_id' => User::factory() or use relationship methods like has() and for() when creating models.
Faker is a PHP library that generates fake data. It can create names, addresses, text, dates, numbers, emails, and much more for realistic test data.
Be very careful! Only seed essential data (like admin users, basic categories) and always use real, secure passwords. Never use Faker in production seeding.
Factory states allow you to define variations of your base factory, like admin(), unverified(), or published() to create models with specific attribute sets.
Use DB::table()->insert() for mass insertion, disable model events, use transactions, and seed in batches to improve performance with large datasets.
# Seeding commands
php artisan db:seed
php artisan db:seed --class=UserSeeder
php artisan migrate:fresh --seed
php artisan migrate:refresh --seed
# Factory commands (in Tinker)
User::factory()->count(10)->create()
User::factory()->admin()->create()
Post::factory()->count(5)->forUser($user)->create()
# Make commands
php artisan make:seeder UserSeeder
php artisan make:factory UserFactory
php artisan make:factory PostFactory --model=Post
You're now equipped to populate your Laravel applications with realistic, useful test data using seeders and factories! In our next post, we'll explore Laravel Middleware: How to Filter HTTP Requests (With Examples) to learn how to intercept and filter HTTP requests.