Installation:
composer require cafe-culture/blog-bundle
Add to config/bundles.php:
return [
// ...
CafeCulture\BlogBundle\CafeCultureBlogBundle::class => ['all' => true],
];
Database Migration: Run migrations (fixtures included):
php bin/console doctrine:migrations:migrate
php bin/console cafe:fixtures:load
First Use Case:
php bin/console cafe:article:create --title="Ethiopian Yirgacheffe" --origin="Ethiopia" --roast="Light" --tags="Floral,Fruity"
/article/1 (auto-generated slug).Content Creation:
CafeArticle entity with specialized fields:
$article = new CafeArticle();
$article->setTitle('Brazilian Santos')
->setOrigin('Brazil')
->setAromaProfile(['Chocolate', 'Nutty'])
->setReadingTime(5); // minutes
{{ article|reading_time('min') }} min read
{{ article|stars(4) }} <!-- Renders ★★★★☆ -->
API Integration:
$recent = $this->get('cafe.api.article_recent')->getRecent(5);
fetch(`/api/cafe/rate/123`, { method: 'POST', body: JSON.stringify({ rating: 5 }) });
Search & Autocomplete:
CafeSearchHandler service:
$results = $this->get('cafe.search')->search('Colombian', ['origin']);
// assets/controllers/autocomplete_controller.js
connect() {
this.element.addEventListener('input', (e) => {
fetch(`/api/cafe/search?q=${e.target.value}`)
.then(r => r.json())
.then(this.displayResults.bind(this));
});
}
Moderation System:
$comment->setFlagged(true);
$comment->setModerationReason('Spam');
$em->persist($comment);
/admin/cafe/comments.CafeArticle via inheritance:
class CustomArticle extends CafeArticle {
#[ORM\Column]
private ?string $brewingMethod = null;
public function getBrewingMethod(): ?string { return $this->brewingMethod; }
}
cafe.article.published:
#[AsEventListener(event: 'cafe.article.published', method: 'onArticlePublished')]
public function onArticlePublished(CafeArticleEvent $event) {
// Send Slack notification, etc.
}
templates/cafe/ (e.g., article/show.html.twig).IP-Based Rating Lockout:
CafeRating entity blocks duplicate ratings per IP. Test locally with:
php bin/console cafe:rating:reset
config/packages/cafe.yaml:
cafe:
rating:
test_ips: ['127.0.0.1', '::1']
Full-Text Search Quirks:
fts or match support. For SQLite, disable in config/packages/doctrine.yaml:
dbal:
types:
cafe_searchable: ~ # Disables searchable fields
Fixture Overwrites:
cafe:fixtures:load. To reset:
php bin/console doctrine:database:drop --force
php bin/console doctrine:database:create
php bin/console cafe:fixtures:load --reset
API Caching:
config/packages/cafe.yaml:
cafe:
api:
cache_ttl: 3600 # 1 hour (default)
{{ dump(_context.get('cafe.twig').getFunctions()) }}
php bin/console debug:event-dispatcher
cafe.* events.Custom Entities:
CafeArticle via CafeArticleExtension trait:
use CafeCulture\BlogBundle\Entity\Extension\CafeArticleExtension;
class ExtendedArticle extends CafeArticle {
use CafeArticleExtension;
#[ORM\Column(nullable: true)]
private ?string $cuppingNotes;
}
Moderation Workflow:
CafeCommentModerationHandler to add custom approval logic:
public function shouldApprove(CafeComment $comment): bool {
return parent::shouldApprove($comment) && $comment->getAuthor()->isTrusted();
}
API Endpoints:
cafe.api.routes YAML config:
# config/routes/cafe.yaml
cafe_api_custom:
path: /api/cafe/custom
controller: CafeCulture\BlogBundle\Controller\Api\CustomController::index
Asset Overrides:
vendor/cafe-culture/blog-bundle/assets/ to public/build/cafe/ and updating config/packages/cafe.yaml:
cafe:
assets:
base_path: /build/cafe
CafeArticleManager to update multiple articles:
$manager->setRoastLevel('Medium', ['1', '2', '3']); // IDs
{% include 'CafeCultureBlogBundle::partials/opengraph.html.twig' %}
origin, roast_level) via trans:
{{ 'cafe.origin.ethiopia'|trans }}
translations/cafe.en.yaml:
cafe:
origin:
ethiopia: "Ethiopia (Yirgacheffe, Sidamo)"
How can I help you explore Laravel packages today?