Database

Confetti CMS features a built-in Object-Relational Mapping (ORM) system that simplifies database interactions.

Since Confetti knows which fields are needed, it fetches all the data in a single query. This eliminates the common n+1 query problem, ensuring fast performance without extra effort, so you can focus on building your website while the ORM handles efficient data management in the background.

Basics

For example, consider a list with titles created as follows:

@php($page = newRoot(new \model\page))
@foreach ($page->list('block')->get() as $block)
    {{ $block->text('title') }}
@endforeach

With automatically generated models and query classes, you can retrieve data such as the title of the first block using:

{{ \model\page\block_list::query()->first()->title }}

You can even add new fields:

@php($block = \model\page\block_list::query()->first())
{{ $block->text('description') }}

Note: In PhpStorm, the generated code only becomes visible after it has been indexed. This happens when you switch windows.

Query Methods

The ORM provides several methods to filter and manipulate queries:

First

first() Retrieve the first item in the list.

$block = \model\page\block_list::query()->first();

Where

where(string|ComponentStandard $key, string $operator, mixed $value) Filter the list based on a specified condition.

$blocks = \model\page\block_list::query()->where('is_active', '=', true)->get();

Alternatively, use the automatically generated banner is_active class:

use \model\homepage\banner_list\is_active;
$blocks = \model\page\block_list::query()->where(new is_active, '=', true)->get();

Order By

orderAscBy(string|ComponentStandard $key) Sort the list in ascending or descending order. Ascending is the default.

$blocks = \model\page\block_list::query()->orderAscBy('title')->get();
$blocks = \model\page\block_list::query()->orderDescBy('title')->get();

Use ->sortable() to enable user-driven sorting in the admin panel. Default order is descending.

Limit

limit(int $limit) Restrict the number of items returned.

$blocks = \model\page\block_list::query()->limit(5)->get();

Offset

offset(int $offset) Skip a specified number of items before returning results.

$blocks = \model\page\block_list::query()->offset(5)->get();

Example: Reusing List

$banner = $homepage->list('banner')->first();

{{ $banner->text('title') }}

{{ $banner->title }}

Example: Nested Foreach Loops

@foreach($homepage->list('block')->get() as $banner)
    {{ $banner->text('title') }}
    @foreach($banner->list('carousel')->sortable()->get() as $carousel)
        {!! $carousel->image('image')->getPicture() !!}
    @endforeach
@endforeach

Example: Multiple Pages & Pagination

Paginated Blog Posts List File: view/blog_overview.blade.php

@php
    $perPage = 10;
    $page = request()->parameter('page') ?: 1;
    $offset = ($page - 1) * $perPage;
    $blogPage = newRoot(new \model\blog_overview);
    $blogs = $blogPage->list('blog')->columns(['title'])->limit($perPage)->offset($offset)->get();
@endphp
<div class="bg-gray-50 py-8">
    <ul class="space-y-8 max-w-4xl mx-auto">
        @foreach($blogs as $blog)
            <li class="bg-white rounded-lg shadow p-6">
                <div class="flex justify-between items-start">
                    <h3 class="text-2xl font-semibold text-blue-500">{{ $blog->text('title')->min(1)->max(50)->bar(['b', 'i', 'u']) }}</h3>
                </div>
                <div class="mt-4">
                    <a href="/blogs/{{ $blog->text('slug')->min(1)->max(50) }}" class="text-blue-500">Read more</a>
                </div>
            </li>
        @endforeach
    </ul>
    <div class="mt-8 flex justify-center">
        @if($page > 1)
            <a href="{{ request()->uri() }}?page={{ $page-1 }}" class="bg-blue-500 text-white px-4 py-2 rounded-lg mr-2">Previous</a>
        @endif
        @if(count($blogs) === $perPage && $blogPage->blogs()->offset($offset + 1)->first() !== null)
            <a href="{{ request()->uri() }}?page={{ $page+1 }}" class="bg-blue-500 text-white px-4 py-2 rounded-lg">Next</a>
        @endif
    </div>
</div>

Blog Post Detail Page

File: view/blog_detail.blade.php

@php
    $alias = str_replace('/blogs/', '', request()->uri());
    $blog = \model\blog_overview\blog_list::query()->whereSlugIs($alias)->first();
@endphp

<main class="max-w-3xl mx-auto">
    <article class="relative pt-12">
        <a href="/blogs" class="bg-blue-500 text-white px-4 py-2 rounded-lg mr-2">Back to overview</a>
        <div class="rounded-lg p-4 text-xl flex justify-center m-8">
            <h1>{{ $blog->title }}</h1>
        </div>
        <div class="font-body">
            <div class="mx-4 w-full">
                {!! $blog->image('image')->widthPx(800)->getPicture(class: 'relative w-full sm:w-220 p-3 rounded-lg') !!}
                @include('website.includes.blocks.index', ['model' => $blog->content('content')])
            </div>
        </div>
    </article>
</main>

Note how the blog is queried using the ->query() method on the \model\blog_overview\blog_list class. You can reuse fields like ->title or access new components such as $blog->image('image').

Registering Blog Post URLs

In the view/index.blade.php file, you can register blog post URLs using a basic routing system:

@switch(true)
    @case(request()->uri() === '/blogs')
        @include('view.blog_overview')
        @break
    @case(str_starts_with(request()->uri(), '/blogs/'))
        @include('view.blog_detail')
        @break
    // ...
@endswitch
No more than once a month
Newsletter