Blog

  • A Developer’s Guide to Soft Deletes in Laravel and Livewire 3

    Hey developers! Are you looking for a smarter way to handle data deletion in your Laravel applications? Instead of permanently erasing records, what if you could just “hide” them, keeping them safe for future recovery or auditing?

    That’s exactly what Soft Deletes are for.

    This powerful Eloquent feature is a lifesaver in many projects, especially when combined with the dynamic magic of Livewire 3. In this guide, we’ll break down exactly what soft deletes are, how to implement them step-by-step, and how to manage them seamlessly in a Livewire component.

    Let’s dive in!

    What is a Soft Delete?

    A soft delete is a way to “delete” a record without actually removing it from your database table.

    When you perform a soft delete on a model, Laravel doesn’t run a DELETE SQL statement. Instead, it simply sets a timestamp on a special deleted_at column in that record’s row.

    Once that deleted_at column is set, Laravel’s Eloquent ORM automatically filters that record out of all your queries. For your application, it’s gone. But for your database, it’s still there, just marked as inactive.

    This is perfect for:

    • Restoring accidentally deleted data.
    • Maintaining a history of records for auditing.
    • Preserving relationships with other tables.

    How to Implement Soft Deletes in Laravel

    Getting started with soft deletes is incredibly simple. It’s a two-step process.

    Step 1: The Migration

    First, you need to add the deleted_at column to your table. The easiest way is to use the softDeletes() helper method in your migration file when creating or altering a table.

    Let’s imagine we have a products table.

    PHP

    <?php
    
    use Illuminate\Database\Migrations\Migration;
    use Illuminate\Database\Schema\Blueprint;
    use Illuminate\Support\Facades\Schema;
    
    return new class extends Migration
    {
        /**
         * Run the migrations.
         */
        public function up(): void
        {
            Schema::create('products', function (Blueprint $table) {
                $table->id();
                $table->string('name');
                $table->decimal('price', 8, 2);
                $table->timestamps();
                $table->softDeletes(); // This adds the nullable `deleted_at` column
            });
        }
    
        /**
         * Reverse the migrations.
         */
        public function down(): void
        {
            Schema::dropIfExists('products');
        }
    };
    

    Now, run your migration: php artisan migrate

    Step 2: The Model

    Next, you need to tell your Eloquent model to use the soft delete functionality. You do this by adding the SoftDeletes trait to the model class.

    PHP

    <?php
    
    namespace App\Models;
    
    use Illuminate\Database\Eloquent\Factories\HasFactory;
    use Illuminate\Database\Eloquent\Model;
    use Illuminate\Database\Eloquent\SoftDeletes; // 1. Import the trait
    
    class Product extends Model
    {
        use HasFactory, SoftDeletes; // 2. Use the trait
    
        protected $fillable = [
            'name',
            'price',
        ];
    }
    

    And that’s it! Any time you call the delete() method on a Product model, it will now perform a soft delete.

    Bringing it to Life with Livewire 3

    Now for the fun part. Let’s create a dynamic interface to manage our products using Livewire 3, allowing a user to delete a product without a page refresh.

    Here’s a simple component that lists products and has a delete button for each one.

    The Livewire Component Class

    PHP

    <?php
    // app/Livewire/ManageProducts.php
    
    namespace App\Livewire;
    
    use Livewire\Component;
    use App\Models\Product;
    use Livewire\Attributes\On; // Import the On attribute
    
    class ManageProducts extends Component
    {
        // This will hold our collection of products from the database
        public function getProductsProperty()
        {
            return Product::orderBy('name')->get();
        }
    
        // The delete method, which will be called from the frontend
        public function delete($productId)
        {
            // Find the product by its ID
            $product = Product::find($productId);
    
            // If the product exists, delete it
            if ($product) {
                $product->delete(); // This performs the soft delete
            }
            
            // We don't need to manually refresh the list. 
            // Livewire will automatically re-render the component.
        }
        
        public function render()
        {
            return view('livewire.manage-products');
        }
    }
    

    The Livewire View

    This is the Blade file where we’ll display our table. Notice the wire:click and wire:confirm attributes—these are powerful Livewire features that make our component interactive.

    HTML

    <div>
        <h3 style="margin-bottom: 1rem;">Product List</h3>
    
        <style>
            /* Simple styling for a clean table */
            .product-table { width: 100%; border-collapse: collapse; }
            .product-table th, .product-table td { border: 1px solid #ddd; padding: 8px; text-align: left; }
            .product-table th { background-color: #f2f2f2; }
            .delete-btn { background-color: #dc3545; color: white; border: none; padding: 5px 10px; border-radius: 4px; cursor: pointer; }
            .delete-btn:hover { background-color: #c82333; }
        </style>
    
        <table class="product-table">
            <thead>
                <tr>
                    <th>Product Name</th>
                    <th>Price</th>
                    <th>Actions</th>
                </tr>
            </thead>
            <tbody>
                @forelse ($this->products as $product)
                    <tr wire:key="{{ $product->id }}">
                        <td>{{ $product->name }}</td>
                        <td>${{ number_format($product->price, 2) }}</td>
                        <td>
                            <button 
                                class="delete-btn"
                                wire:click="delete({{ $product->id }})"
                                wire:confirm="Are you sure you want to delete this product?">
                                Delete
                            </button>
                        </td>
                    </tr>
                @empty
                    <tr>
                        <td colspan="3">No products found.</td>
                    </tr>
                @endforelse
            </tbody>
        </table>
    </div>
    

    Now, when a user clicks the “Delete” button, Livewire will show a confirmation dialog. If confirmed, it will call the delete() method in your component, which soft deletes the record. The component will then automatically re-render, and the product will disappear from the list—all without a single page reload!

    Querying Soft Deleted Models

    What if you need to see the “deleted” items? Eloquent provides handy methods for that:

    • withTrashed(): Get all records, including soft-deleted ones.PHP$allProducts = Product::withTrashed()->get();
    • onlyTrashed(): Get only the soft-deleted records. This is great for building a “Trash” or “Recycle Bin” feature.PHP$deletedProducts = Product::onlyTrashed()->get();
    • restore(): To bring a soft-deleted record back.PHPProduct::withTrashed()->find($productId)->restore();
    • forceDelete(): To permanently delete the record. Use with caution!PHPProduct::withTrashed()->find($productId)->forceDelete();

    Conclusion: To Soft Delete or Not?

    Soft deletes are a fantastic tool in the Laravel ecosystem. They provide a safety net for data, maintain historical integrity, and are essential for applications that require auditing. Combined with Livewire, you can build a user-friendly and robust experience for managing data.

    However, remember the trade-offs: they increase your database size and can add complexity to data privacy rules like GDPR. Always evaluate if they fit your project’s specific needs.

    What are your thoughts? Do you use soft deletes in your projects? Share your experiences in the comments below!

  • Laravel 12: Sail vs. Valet – The Key Differences & When to Use Each

    With the release of Laravel 12, choosing a local development environment remains a crucial factor in a developer’s productivity. Two of the most popular and officially supported options by Laravel are Sail and Valet. Although they both serve the same fundamental purpose, they operate in drastically different ways, each with its own advantages and ideal use cases.

    The core distinction lies in the approach: Sail uses Docker containers to create an isolated and consistent development environment, while Valet directly configures your macOS to run Nginx and DnsMasq in the background, offering a minimalist, high-speed solution.

    Laravel Sail: The Consistent and Portable Docker Environment

    Laravel Sail is a lightweight command-line interface for interacting with Laravel’s default Docker development environment. Essentially, each project gets its own isolated environment defined in a docker-compose.yml file. This means services like PHP, MySQL, and Redis run in their own containers, without interfering with other projects or software installed on your local machine.

    Sail’s Key Features:

    • Complete Isolation: Each project has its own dependencies and configurations, eliminating conflicts between different versions of PHP or other tools.
    • Environment Consistency: It ensures that all team members and the production environment are as similar as possible, reducing surprises at deployment time.
    • Cross-Platform: Works consistently across macOS, Windows (via WSL2), and Linux.
    • Customizable: You can easily customize the docker-compose.yml file to add or remove services as your project requires.

    When should you use Laravel Sail?

    • Team Collaboration: It’s ideal for ensuring all developers work with the exact same environment and software versions.
    • Projects with Complex Dependencies: Perfect for when a project requires specific PHP versions, extensions, or other services that might conflict with other projects.
    • Windows or Linux Users: It is the official and most robust solution for these operating systems.
    • Production Parity: If your production environment is also container-based, Sail offers a more aligned development workflow.

    Laravel Valet: Speed and Simplicity on macOS

    Laravel Valet is a minimalist development environment exclusively for macOS. It configures your Mac to run Nginx and DnsMasq in the background, allowing you to serve your sites with incredible speed. After installation, you simply “park” a directory (valet park), and every Laravel project within that directory becomes automatically available in your browser under a .test domain.

    Valet’s Key Features:

    • Extremely Fast: With no Docker overhead, Valet is noticeably faster when loading pages.
    • Minimal Resource Consumption: It uses a very small amount of RAM.
    • Zero Configuration Per Project: Once installed, there’s no need to start or configure anything for new projects.
    • macOS Integration: It works seamlessly, starting its services automatically when you boot your system.

    When should you use Laravel Valet?

    • Solo Developers on macOS: It’s an incredibly simple and fast solution for anyone working alone on a Mac.
    • Simple Projects: Ideal for projects that don’t have highly specific or conflicting software requirements.
    • Focus on Speed: If local development performance is your top priority.
    • Multiple Small Projects: The ease of just creating a folder and having a site ready to go is a major advantage.

    Comparison Chart: Sail vs. Valet

    FeatureLaravel SailLaravel Valet
    Core TechnologyDockerNginx, DnsMasq
    Operating SystemmacOS, Windows (WSL2), LinuxmacOS only
    IsolationComplete (per-project)Shared (services run on host machine)
    PerformanceGood, but with Docker overheadExcellent, very fast
    Resource UsageHigher (RAM & CPU)Minimal
    Per-Project SetupRequires starting containers (sail up)None
    Team ConsistencyHighLow (depends on each user’s machine)
    Best ForTeams, complex projects, Windows/Linux usersSolo Mac developers, simple projects

    The Rise of Laravel Herd

    It’s important to note that with Laravel 12, the official documentation now prominently features Laravel Herd. Herd is a blazing-fast, native development environment for both macOS and Windows that comes with everything you need, including PHP and Nginx. It builds on the foundation of Valet but provides a graphical user interface and removes the need for dependencies like Homebrew. For new projects on macOS and Windows, Herd is quickly becoming the top recommendation from the Laravel team, serving as a modern and even simpler alternative to Valet.