# Speelgoedruilen.nl bouwen met Laravel: een case study

> Zo werkt ruilplatform Speelgoedruilen.nl: Laravel 13, Inertia en Vue, realtime chat met Reverb, AI-fotocontrole, Mollie en lokale SEO per woonplaats.

- URL: https://www.snoerendevelopment.nl/blog/speelgoedruilen-platform-bouwen-met-laravel
- Language: nl-NL
- Author: Michael Snoeren (Snoeren Development)
- Gepubliceerd: 2026-10-16

[Speelgoedruilen.nl](https://speelgoedruilen.nl) is een Nederlands platform waar gezinnen tweedehands speelgoed met elkaar ruilen in plaats van nieuw te kopen. Ik heb het van idee tot productie zelf gebouwd met Laravel 13, Inertia met Vue, Laravel Reverb voor realtime chat, Filament voor beheer en Mollie voor betaalde abonnementen. In deze case study laat ik zien welke keuzes ik maakte en waarom, zodat je ziet wat er bij een marktplaats of community-platform komt kijken.

**Kort samengevat**

- **Wat:** een ruilplatform voor speelgoed met profielen, advertenties, chat, ruilverzoeken, reviews en een Plus-abonnement.
- **Stack:** Laravel 13 op PHP 8.5, Inertia 3 met Vue en Tailwind CSS 4, Filament 5, Laravel Reverb, Laravel Cashier Mollie, Spatie Media Library en Laravel Nightwatch.
- **Opvallend:** een foto is genoeg om speelgoed aan te bieden. AI controleert de foto en stelt naam, beschrijving, staat en categorieën voor.
- **Groei:** pagina's per woonplaats, zoekmeldingen per e-mail en directe meldingen aan zoekmachines via IndexNow.

## Waarom een ruilplatform een goede test is voor je architectuur

Een marktplaats lijkt eenvoudig: gebruikers plaatsen iets, anderen reageren. In de praktijk raakt zo'n platform bijna alles wat een webapplicatie lastig maakt. Je hebt uploads van foto's, realtime berichten, e-mails, betalingen, moderatie, privacy en vindbaarheid. En omdat kinderen met hun ouders meekijken, moet de inhoud bovendien veilig zijn.

Daarom heb ik vanaf dag één gekozen voor de standaardonderdelen van het Laravel-ecosysteem in plaats van zelfgebouwde oplossingen. Hoe minder maatwerk in de basis, hoe meer tijd er overblijft voor de dingen die het product onderscheiden.

## De stack: Laravel 13, Inertia en Vue

De applicatie draait op **Laravel 13** en **PHP 8.5**. De frontend is gebouwd met **Inertia 3** en **Vue**, gestyled met **Tailwind CSS 4**. Met Inertia schrijf je gewone Laravel-controllers, terwijl de gebruiker een snelle single-page app ervaart. Je hebt geen aparte API nodig voor je eigen frontend, en routes en validatie blijven op één plek.

Een paar keuzes die veel tijd schelen:

- **Laravel Wayfinder** genereert TypeScript-functies voor routes, zodat een gewijzigde URL meteen een fout geeft in de frontend in plaats van een kapotte link in productie.
- **Spatie Laravel Data** met de TypeScript-transformer zorgt dat de data die een controller naar Vue stuurt ook in TypeScript getypeerd is.
- **Optimistic updates van Inertia** laten een bericht of een wijziging direct in beeld zien, nog voordat de server reageert. Mislukt het, dan wordt het netjes teruggedraaid.

## Realtime chat en ruilverzoeken met Laravel Reverb

Ruilen begint met een gesprek. Gebruikers chatten met elkaar en sturen vanuit dat gesprek een ruilverzoek, waarin ze kiezen welk eigen speelgoed ze ertegen ruilen. Wordt het verzoek geaccepteerd, dan staat het speelgoed van beide kanten meteen op geruild.

De chat draait op **Laravel Reverb**, de WebSocket-server van Laravel zelf. Berichten komen zo direct binnen zonder dat je afhankelijk bent van een externe dienst als Pusher. Een nieuw bericht verschijnt meteen, iets lichter totdat het is opgeslagen. Lukt het versturen niet, dan staat de tekst gewoon weer in het invoerveld.

Bij elk ruilverzoek hoort een e-mail. Om te voorkomen dat iemand anderen daarmee overspoelt, gelden er per account limieten. Laravel maakt dat eenvoudig met de ingebouwde rate limiter:

```php
RateLimiter::for('trades', function (Request $request): array {
    $key = $request->user()?->id ?? $request->ip();

    return [
        Limit::perMinute(2)->by("trades:minute:{$key}"),
        Limit::perDay(20)->by("trades:day:{$key}"),
    ];
});
```

Door twee limieten te combineren, stop je zowel een snelle reeks als misbruik over de hele dag. Voor chatberichten, meldingen, het contactformulier en de fotocontrole gelden vergelijkbare limieten.

## AI-fotocontrole: van foto naar advertentie

De grootste drempel bij een marktplaats is het plaatsen van een advertentie. Niemand heeft zin om een titel, beschrijving en categorie te bedenken voor een doos Duplo. Daarom is op Speelgoedruilen.nl een foto genoeg.

Na het uploaden beoordeelt een AI-model (Claude van Anthropic) de foto. Het controleert of de foto past op een platform voor gezinnen en of er echt speelgoed op staat. Daarnaast stelt het een korte naam, een feitelijke beschrijving, de staat en maximaal drie categorieën voor. De gebruiker hoeft alleen nog te controleren en op plaatsen te drukken.

Een paar ontwerpkeuzes maken dit betrouwbaar in productie:

- **Gestructureerde output.** Het model antwoordt volgens een vast JSON-schema, waarin de categorieën beperkt zijn tot de categorieën die echt in de database staan. Er komen dus geen verzonnen categorieën binnen.
- **Feitelijk, geen verkooptaal.** De instructies verbieden aanprijzingen, uitroeptekens en prijzen. Er wordt geruild, niet verkocht.
- **Caching op basis van de foto.** Het resultaat wordt 24 uur bewaard onder een hash van de afbeelding. Dezelfde foto opnieuw uploaden kost geen nieuwe API-aanroep.
- **Fail-open.** Is de API niet bereikbaar, dan wordt de fout gelogd en kan de gebruiker gewoon zelf de velden invullen. Een storing bij een externe dienst mag nooit het aanbieden blokkeren.
- **Testbaar.** De API-aanroepen lopen via de HTTP-client van Laravel, zodat ze in Pest-tests met `Http::fake()` te simuleren zijn.

```php
try {
    $analysis = $this->request($key, $image, $categories);
} catch (\Throwable $exception) {
    // Een storing van de API mag het aanbieden niet blokkeren.
    report($exception);

    return ImageAnalysis::unknown();
}
```

## Abonnementen met Mollie en beheer met Filament

Het gratis account heeft limieten op het aantal speelgoed en ruilverzoeken per maand. Met **Plus** (€ 2,99 per maand of € 24,99 per jaar) gelden hogere limieten en zie je nieuw speelgoed eerder. De betalingen lopen via **Laravel Cashier Mollie**, inclusief facturen met adres, btw- en KvK-nummer. De limieten en prijzen staan in de configuratie, zodat de tekst op de website, de abonnementspagina en in de algemene voorwaarden automatisch overeenkomt.

Het beheer is gebouwd met **Filament 5**. Beheerders zien widgets met ruilverzoeken en omzet, behandelen meldingen van gebruikers en kunnen tijdelijk inloggen als een gebruiker om problemen na te lopen. Belangrijke gebeurtenissen, zoals een nieuwe account of een nieuw abonnement, komen via een webhook ook binnen in Discord.

## Vindbaarheid: lokale SEO per woonplaats

Speelgoed ruil je het liefst bij iemand in de buurt. Daarom heeft elke woonplaats een eigen pagina, zoals `/in/utrecht`, met het speelgoed dat daar wordt aangeboden. Dat sluit precies aan bij hoe ouders zoeken: "speelgoed ruilen in Utrecht".

Om die pagina's snel in zoekmachines te krijgen, wordt nieuw, gewijzigd en verwijderd speelgoed elk uur gemeld via **IndexNow**. Bing en andere zoekmachines werken die pagina's daardoor sneller bij. Voor Google is er de sitemap. Daarnaast heeft het platform een `llms.txt`, zodat AI-assistenten zoals ChatGPT en Claude het platform goed kunnen uitleggen als iemand ernaar vraagt.

Gebruikers kunnen per woonplaats een zoekmelding aanzetten. Ze krijgen dan hoogstens één keer per dag een e-mail met het nieuwe speelgoed in die plaats. Dat houdt bezoekers betrokken zonder dat je ze overspoelt.

## Veiligheid, privacy en onderhoud

Een platform voor gezinnen vraagt om extra zorg. Een paar maatregelen die ik heb ingebouwd:

- **Inloggen** met tweestapsverificatie of passkeys via Laravel Passkeys.
- **Misbruik beperken** met maximaal één registratie per dag en vijf nieuwe advertenties per dag per account.
- **Opruimen** van onbevestigde accounts na 7 dagen, en van verwijderd speelgoed en gesprekken na 30 dagen via `model:prune`.
- **Statistieken** met Matomo, gehost in Nederland, waarbij alleen een account-ID wordt meegestuurd en geen naam of e-mailadres.
- **Monitoring** met Laravel Nightwatch, zodat fouten en trage queries zichtbaar zijn voordat gebruikers erover klagen.

De kwaliteit bewaak ik met **Pest**, inclusief browsertests in Firefox en Safari en mutatietests, aangevuld met PHPStan via Larastan. Elke wijziging komt in een changelog, en de skill `/changelog` vertaalt die naar een pagina "Wat is er nieuw" in gewone taal voor gebruikers.

## Wat je hiervan kunt meenemen voor je eigen platform

1. **Gebruik het ecosysteem.** Reverb, Cashier, Filament en Nightwatch besparen weken werk ten opzichte van zelfbouw.
2. **Haal drempels weg.** Een foto in plaats van een formulier maakt het verschil tussen een lege en een levende marktplaats.
3. **Laat externe diensten nooit je kernproces blokkeren.** Ontwerp AI en andere integraties zo dat ze stil terugvallen.
4. **Denk vanaf het begin aan vindbaarheid.** Lokale pagina's, IndexNow en `llms.txt` kosten weinig en leveren structureel bezoekers op.
5. **Zet limieten op alles wat e-mail of kosten veroorzaakt.** Rate limiting is in Laravel een paar regels code.

## Veelgestelde vragen

### Welke techniek gebruikt Speelgoedruilen.nl?

Speelgoedruilen.nl draait op Laravel 13 en PHP 8.5, met Inertia en Vue voor de frontend, Laravel Reverb voor realtime chat, Filament voor het beheer en Laravel Cashier Mollie voor abonnementen.

### Hoe werkt de AI-fotocontrole?

Na het uploaden controleert een AI-model of de foto geschikt is voor gezinnen en speelgoed laat zien. Het stelt een naam, beschrijving, staat en categorieën voor, die de gebruiker kan aanpassen. Valt de AI uit, dan vult de gebruiker de velden zelf in.

### Is Laravel geschikt voor een marktplaats of community-platform?

Ja. Laravel heeft officiële pakketten voor realtime communicatie, betalingen, authenticatie en monitoring, en met Filament bouw je snel een beheeromgeving. Daardoor ga je snel live met een MVP en kan het platform daarna doorgroeien.

## Zelf een platform laten bouwen?

Heb je een idee voor een marktplaats, community of SaaS-product? Ik bouw platformen met Laravel van idee tot productie, vanuit Oosterhout voor klanten in heel Nederland. Bekijk wat ik doe bij [SaaS-platformen bouwen](/diensten/saas-platformen-bouwen) of [een MVP laten bouwen](/diensten/mvp-laten-bouwen), of [neem contact op](/contact) om je idee te bespreken.