courtyard/forum
Courtyard Forum is the core package of an event-driven forum platform for developers. Built to be stable, scalable, and extensible, it provides a clean API to build custom communities, with optional Symfony2 integration via ForumBundle.
Installation:
composer require courtyard/forum
Note: Due to Symfony2 dependencies, ensure your composer.json includes:
"require": {
"symfony/event-dispatcher": "^2.0",
"symfony/security": "^2.0"
}
Basic Laravel Integration:
Create a ForumServiceProvider in app/Providers/:
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Courtyard\Forum\ForumBundle;
class ForumServiceProvider extends ServiceProvider
{
public function register()
{
// Override Symfony's EventDispatcher with Laravel's
$this->app->singleton('event_dispatcher', function ($app) {
return $app['events'];
});
// Bind Courtyard services (adjust based on package structure)
$this->app->bind('courtyard.forum.manager', function ($app) {
return new \Courtyard\Forum\Manager\ThreadManager(
$app['event_dispatcher'],
$app['courtyard.forum.repository.thread']
);
});
}
}
First Use Case: Create a Thread
Register the provider in config/app.php and create a controller:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Courtyard\Forum\Entity\Thread;
class ThreadController extends Controller
{
public function store(Request $request)
{
$thread = new Thread();
$thread->setTitle($request->title);
$thread->setContent($request->content);
$thread->setAuthor($request->user()); // Assuming Laravel auth
// Use the manager (adjust based on actual package methods)
$manager = app('courtyard.forum.manager');
$manager->create($thread);
return redirect()->route('threads.show', $thread->getId());
}
}
Verify Events:
Listen for the thread.created event in EventServiceProvider:
protected $listen = [
'thread.created' => [
'App\Listeners\SendThreadCreatedNotification',
],
];
src/Courtyard/Forum/ for:
Entity/ (Thread, Post, User models)Manager/ (ThreadManager, PostManager)Event/ (thread.created, post.updated, etc.)ForumBundle.php for service configurations and dependencies.tests/ for usage patterns (though likely Symfony-focused).Event-Driven Workflows:
// Example: Listen for post upvotes
event(new PostUpvoted($post));
// In EventServiceProvider
protected $listen = [
'post.upvoted' => [
'App\Listeners\UpdateUserReputation',
],
];
Repository Pattern:
$threadRepo = app('courtyard.forum.repository.thread');
$threads = $threadRepo->findBy(['tag' => 'laravel']);
class ThreadRepository extends \Illuminate\Database\Eloquent\Model
{
protected $table = 'forum_threads';
// ...
}
Service Layer:
$threadManager = app('courtyard.forum.manager');
$thread = $threadManager->create($threadEntity);
$this->app->bind('courtyard.forum.manager', function ($app) {
return new \App\Services\ForumManager(
$app['event_dispatcher'],
new \App\Repositories\ThreadRepository
);
});
Entity Mapping:
class Thread extends \Illuminate\Database\Eloquent\Model
{
protected $fillable = ['title', 'content', 'author_id'];
public static function fromCourtyardEntity(\Courtyard\Forum\Entity\Thread $entity)
{
return self::create([
'title' => $entity->getTitle(),
'content' => $entity->getContent(),
'author_id' => $entity->getAuthor()->getId(),
]);
}
}
Thread Creation Workflow:
title, content).Thread entity (Courtyard or Laravel model).thread.created event.$thread = new \App\Models\Thread([
'title' => $request->title,
'content' => $request->content,
'author_id' => auth()->id(),
]);
$thread->save();
event(new \Courtyard\Forum\Event\ThreadCreated($thread));
Moderation Workflow:
// Check if user is a moderator
if (auth()->user()->can('moderate')) {
$post->setStatus('hidden');
$postManager = app('courtyard.forum.manager.post');
$postManager->update($post);
}
Real-Time Updates:
// In a listener for post.created
broadcast(new PostCreated($post))->toOthers();
Authentication:
Security with Laravel’s Auth:
$user = auth()->user(); // Instead of $security->getUser()
Database:
Schema::create('forum_threads', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('content');
$table->foreignId('author_id')->constrained('users');
$table->timestamps();
});
Testing:
Tests\TestCase:
public function test_thread_creation()
{
$response = $this->post('/threads', [
'title' => 'Test Thread',
'content' => 'Hello world',
]);
$response->assertRedirect();
$this->assertDatabaseHas('forum_threads', ['title' => 'Test Thread']);
}
Frontend:
public class ThreadComponent extends Component
{
public $thread;
public function mount($id)
{
$this->thread = \App\Models\Thread::findOrFail($id);
}
public function render()
{
return view('livewire.thread');
}
}
Symfony Dependencies:
HttpFoundation, Security).$this->app->bind('request', function () {
return Request::capture();
});
Event Dispatcher Mismatch:
EventDispatcher differs from Laravel’s Dispatcher.class LaravelEventDispatcher implements \Symfony\Component\EventDispatcher\EventDispatcherInterface
{
protected $dispatcher;
public function __construct(\Illuminate\Events\Dispatcher $dispatcher)
{
$this->dispatcher = $dispatcher;
}
public function dispatch($event, $eventName = null)
{
$this->dispatcher->dispatch($event);
}
}
Entity vs. Model Confusion:
Archived Status:
How can I help you explore Laravel packages today?