Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Davieboy's avatar

Collections problem

Been following the forum tutorial and ran into problems after fixing those problems i have created another and i cant just get my head round it,

on my main forum page the ability to filter by channel is failing also same problem on create a post as it doesn't fetch the channels, instead i get this type of error

Facade\Ignition\Exceptions\ViewException: Undefined variable: channels (View: /var/www/resources/views/threads/index.blade.php)

the URL 's in question is /forum and /forum/create

the code on threads/index.blade.php and threads/index.blade.php

@foreach ($channels as $channel)
    <a class="dropdown-item" href="/forum/{{ $channel->slug }}">{{ $channel->name}}</a>
@endforeach

web.php

Route::delete('/forum/replies/{reply}', ['middleware' => 'auth', 'uses' => 'RepliesController@destroy']);
Route::get('/home', 'HomeController@index')->name('home');
Route::get('/forum', ['middleware' => 'auth', 'uses' => 'ThreadsController@index']);
Route::get('/forum/create', ['middleware' => 'auth', 'uses' => 'ThreadsController@create']);
Route::get('/forum/{channel}/{thread}', ['middleware' => 'auth', 'uses' => 'ThreadsController@show']);
Route::delete('/forum/{channel}/{thread}', ['middleware' => 'auth', 'uses' => 'ThreadsController@destroy']);
Route::post('/forum', ['middleware' => 'auth', 'uses' => 'ThreadsController@store']);
Route::get('forum/{channel}', ['middleware' => 'auth', 'uses' => 'ThreadsController@index']);

Route::post('/forum/{channel}/{thread}/replies', ['middleware' => 'auth', 'uses' => 'RepliesController@store']);
Route::patch('/forum/replies/{reply}', ['middleware' => 'auth', 'uses' => 'RepliesController@update']);

Route::post('/forum/replies/{reply}/favorites', ['middleware' => 'auth', 'uses' => 'FavoritesController@store']);

Route::get('/profiles/{user}', ['middleware' => 'auth', 'uses' => 'ProfilesController@show']);

ThreadsController.php

<?php

namespace App\Http\Controllers;

use App\Filters\ThreadFilters;
use App\Channel;
use App\Thread;
use Illuminate\Http\Request;
use RealRashid\SweetAlert\Facades\Alert;

class ThreadsController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth');
    }

    /**
     * Display a listing of the resource.
     *
     * @param Channel $channel
     * @param ThreadFilters $filters
     * @return \Illuminate\Http\Response
     */
    public function index(Channel $channel, ThreadFilters $filters)
    {
        $threads = $this->getThreads($channel, $filters);

        if (request()->wantsJson()) {
            return $threads;
        }

        return view('threads.index', compact('threads'));
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        return view('threads.create');
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param \Illuminate\Http\Request $request
     * @return \Illuminate\Http\Response
     * @throws \Illuminate\Validation\ValidationException
     */
    public function store(Request $request)
    {
        $this->validate($request, [
            'title' => 'required',
            'body' => 'required',
            'channel_id' => 'required|exists:channels,id'
        ]);

        $thread = Thread::create([
            'user_id' => auth()->id(),
            'channel_id' => request('channel_id'),
            'title' => request('title'),
            'body' => request('body')
        ]);
        /*toast('Your Post has been published!','success');*/
        return redirect($thread->path());

    }

    /**
     * Display the specified resource.
     *
     * @param Channel $channel
     * @param \App\Thread $thread
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\Response|\Illuminate\View\View
     */
    public function show(Channel $channel, Thread $thread)
    {
        return view('threads.show', [
            'thread' => $thread,
            'replies' => $thread->replies()->paginate(20)
        ]);
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  \App\Thread  $thread
     * @return \Illuminate\Http\Response
     */
    public function edit(Thread $thread)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \App\Thread  $thread
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, Thread $thread)
    {
        //
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param \App\Thread $thread
     * @return \Illuminate\Http\Response
     * @throws \Exception
     */
    public function destroy($channel, Thread $thread)
    {
        $this->authorize('update', $thread);

        $thread->delete();

        if (request()->wantsJson()) {
            return response([], 204);
        }

        return redirect('/forum');
    }

    /**
     * @param Channel $channel
     * @param ThreadFilters $filters
     * @return mixed
     */
    protected function getThreads(Channel $channel, ThreadFilters $filters)
    {
        $threads = Thread::latest()->filter($filters);

        if ($channel->exists) {
            $threads->where('channel_id', $channel->id);
        }

        return $threads->get();
    }

}

Thread.php

<?php

namespace App;

use App\Filters\ThreadFilters;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;

class Thread extends Model
{
    use RecordsActivity;

    protected $guarded = [];

    protected $with = ['creator', 'channel'];

    protected static function boot()
    {
        parent::boot();

        static::addGlobalScope('replyCount', function ($builder) {
            $builder->withCount('replies');

        });

        static::deleting(function ($thread) {
            $thread->replies->each->delete();
        });
    }

    public function path()
    {
        return "/forum/{$this->channel->slug}/{$this->id}";
    }

    public function replies()
    {
        return $this->hasmany(Reply::class);
    }

    public function creator()
    {
        return $this->belongsTo(User::class, 'user_id');
    }

    public function channel()
    {
        return $this->belongsTo(Channel::class);
    }

    public function addReply($reply)
    {
        $this->replies()->create($reply);
    }

    /**
     * Apply all relevant thread filters.
     *
     * @param  Builder       $query
     * @param  ThreadFilters $filters
     * @return Builder
     */
    public function scopeFilter($query, ThreadFilters $filters)
    {
        return $filters->apply($query);
    }


}

Channel.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Channel extends Model
{
    public function getRouteKeyName()
    {
        return 'slug';
    }

    public function threads()
    {
        return $this->hasMany(Thread::class);
    }
}

where am i going wrong?

0 likes
2 replies
tykus's avatar
tykus
Best Answer
Level 104

There is nothing the controller that is passing the $channels data to the view. In the AppServiceProvider, you should have a View Composer which is providing the $channels variable to all views:

        \View::composer('*', function ($view) {
            $channels = \Cache::rememberForever('channels', function () {
                return Channel::all();
            });

            $view->with('channels', $channels);
        });

https://github.com/laracasts/Lets-Build-a-Forum-in-Laravel/blob/master/app/Providers/AppServiceProvider.php

Davieboy's avatar

tykus i wish i posted this yesterday morning, thanks man

yeah i forgot to copy that over from the previous project, quite surprised i never watched that lesson again, think i watched every other previous lesson at least twice.

Please or to participate in this conversation.