Many-to-Many Portfolio Categories

User
We have Portfolio , PortfolioCateogires now want Portfolio can many cateogires
Assistant
To associate a `Portfolio` with multiple `PortfolioCategories`, you need to create a **many-to-many relationship** in Laravel. Here’s how you can do that:

### 🧱 Step 1: Create the Pivot Table

Run a migration to create a pivot table, usually named `portfolio_portfolio_category`.

php artisan make:migration create_portfolio_portfolio_category_table

In the migration file:

Schema::create('portfolio_portfolio_category', function (Blueprint $table) {
    $table->id();
    $table->foreignId('portfolio_id')->constrained()->onDelete('cascade');
    $table->foreignId('portfolio_category_id')->constrained()->onDelete('cascade');
    $table->timestamps();
});

Then run:

php artisan migrate

### 🧬 Step 2: Define Relationships in Models

#### In `Portfolio.php`

public function categories()
{
    return $this->belongsToMany(PortfolioCategory::class, 'portfolio_portfolio_category');
}

#### In `PortfolioCategory.php`

public function portfolios()
{
    return $this->belongsToMany(Portfolio::class, 'portfolio_portfolio_category');
}

### 📝 Step 3: Sync or Attach Categories

When saving or updating a portfolio, you can sync categories like this:

$portfolio = Portfolio::find($id);
$portfolio->categories()->sync([1, 2, 3]); // array of category IDs

Or attach:

$portfolio->categories()->attach($categoryId);

Or detach:

$portfolio->categories()->detach($categoryId);

If you're using a form with checkboxes for categories, just collect the selected category IDs and sync them.

Let me know if you're using Nova, Livewire, Inertia, or plain Blade – I can help tailor the form example for that.

User
rescieve form input
Assistant
Perfect! Here's how to **receive form input** for assigning multiple categories to a Portfolio.

I'll show you the setup for:

– A form with checkboxes for categories
– The controller logic to handle form submission

Let’s go 👇

### ✅ 1. **Form Input (Blade Example)**

<form method="POST" action="{{ route('portfolios.store') }}">
    @csrf

    <!-- Portfolio fields -->
    <input type="text" name="title" placeholder="Portfolio Title" required>

    <!-- Portfolio Categories -->
    <label>Select Categories:</label><br>
    @foreach ($categories as $category)
        <label>
            <input type="checkbox" name="categories[]" value="{{ $category->id }}">
            {{ $category->name }}
        </label><br>
    @endforeach

    <button type="submit">Save Portfolio</button>
</form>

Make sure you pass `$categories` to the view from the controller:

$categories = PortfolioCategory::all();
return view('portfolios.create', compact('categories'));

### 🧠 2. **Controller Logic**

In your `PortfolioController`:

public function store(Request $request)
{
    $request->validate([
        'title' => 'required|string|max:255',
        'categories' => 'array',
        'categories.*' => 'exists:portfolio_categories,id',
    ]);

    // Create portfolio
    $portfolio = Portfolio::create([
        'title' => $request->title,
        // add other fields here
    ]);

    // Attach categories
    $portfolio->categories()->sync($request->categories);

    return redirect()->route('portfolios.index')->with('success', 'Portfolio created successfully!');
}

Let me know if you're using **Inertia.js**, **Vue**, **Livewire**, or **Nova**, and I can adjust the form/logic accordingly.

User
also store image
Assistant
Got it! Let's include **image upload** functionality along with saving categories for a Portfolio.

### ✅ 1. **Update HTML Form**

Make sure your form has `enctype="multipart/form-data"` to allow file upload:

<form method="POST" action="{{ route('portfolios.store') }}" enctype="multipart/form-data">
    @csrf

    <input type="text" name="title" placeholder="Portfolio Title" required>

    <!-- Image Upload -->
    <label>Upload Image:</label>
    <input type="file" name="image" required>

    <!-- Categories -->
    <label>Select Categories:</label><br>
    @foreach ($categories as $category)
        <label>
            <input type="checkbox" name="categories[]" value="{{ $category->id }}">
            {{ $category->name }}
        </label><br>
    @endforeach

    <button type="submit">Save Portfolio</button>
</form>

### 🧠 2. **Controller Logic to Handle Image + Categories**

In your `PortfolioController`:

public function store(Request $request)
{
    $request->validate([
        'title' => 'required|string|max:255',
        'image' => 'required|image|mimes:jpeg,png,jpg,webp|max:2048',
        'categories' => 'array',
        'categories.*' => 'exists:portfolio_categories,id',
    ]);

    // Store image
    $imagePath = $request->file('image')->store('portfolios', 'public');

    // Create portfolio with image path
    $portfolio = Portfolio::create([
        'title' => $request->title,
        'image' => $imagePath,
    ]);

    // Attach categories
    $portfolio->categories()->sync($request->categories);

    return redirect()->route('portfolios.index')->with('success', 'Portfolio created!');
}

### 🧾 3. **Migration for Portfolios Table (if needed)**

Make sure your `portfolios` table has an `image` column:

$table->string('image')->nullable();

### 🎨 4. **Displaying the Image (Optional)**

<img src="{{ asset('storage/' . $portfolio->image) }}" alt="{{ $portfolio->title }}" width="200">

Let me know if you'd prefer the image name to be stored custom (with timestamp, slug, etc.), or if you’re using Vue/Inertia instead of Blade for the form.

Leave a Comment

Your email address will not be published. Required fields are marked *