createNestedStructure

Metoda Database::createNestedStructure() převede ploché výsledky z JOIN dotazů do vnořené struktury. Nachází se v Moony\bootstrap\core\helpers\Database.

Lze ji volat přímo nebo přes QueryBuilder: ->createNestedStructure($structure)->getResult()

Příklad — SQL dotaz

Uživatelé → objednávky → logy a změny:

SELECT
    users.id as users_id,
    users.email,
    orders.id as orders_id,
    orders.product_id,
    orderslogs.id as orderslogs_id,
    orderslogs.action as log_action,
    orderschanges.id as orderschanges_id,
    orderschanges.action as change_action,
    orderschanges.old_value,
    orderschanges.new_value
FROM users
    LEFT JOIN orders ON orders.user_id = users.id
    LEFT JOIN orderslogs ON orderslogs.order_id = orders.id
    LEFT JOIN orderschanges ON orderschanges.order_id = orders.id
WHERE users.id = 1

Definice struktury

use Moony\bootstrap\core\helpers\Database;

$nested = Database::createNestedStructure($rows, [
    'users_id',
    'email',
    'orders' => [
        'orders_id',
        'product_id',
        'logs' => [
            'orderslogs_id',
            'log_action'
        ],
        'changes' => [
            'orderschanges_id',
            'change_action',
            'old_value',
            'new_value'
        ]
    ]
]);
První položka v každém poli (i vnořeném) musí být unikátní identifikátor (typicky auto increment ID). Podle něj se záznamy seskupují.

Výsledek

Array
(
    [241] => Array
        (
            [email] => muj@mail.com
            [orders] => Array
                (
                    [5] => Array
                        (
                            [product_id] => 12411
                            [logs] => Array
                                (
                                    [344] => Array ([log_action] => Packet)
                                    [345] => Array ([log_action] => Shipped)
                                )
                            [changes] => Array
                                (
                                    [8452] => Array ([change_action] => Changed price, [old_value] => 299, [new_value] => 199)
                                )
                        )
                    [12] => Array
                        (
                            [product_id] => 32541
                            [logs] => Array (...)
                            [changes] => Array ()
                        )
                )
        )
)

Wildcards

Místo výpisu všech sloupců lze použít wildcard patterny:

* — všechny zbývající sloupce

Na root úrovni zahrne všechny sloupce, které nejsou použité ve vnořených strukturách:

$nested = Database::createNestedStructure($rows, [
    '*',                    // všechny sloupce kromě těch ve vnořených
    'orders' => [
        'orders_id',
        'product_id'
    ]
]);

prefix_* — sloupce podle prefixu

Zahrne všechny sloupce začínající daným prefixem. Prefix se automaticky odstraní z názvu klíče ve výsledku:

// SQL: SELECT comment_id, comment_text, comment_author, comment_date ...
$nested = Database::createNestedStructure($rows, [
    'post_id',
    'title',
    'comments' => [
        'comment_*'         // zahrne comment_id, comment_text, ... jako id, text, ...
    ]
]);

// Výsledek: comments[1] => ['id' => 5, 'text' => '...', 'author' => '...', 'date' => '...']
Wildcard comment_* najde všechny sloupce začínající comment_ a ve výsledku odstraní prefix — comment_text se stane text.

? — volitelné vnořené struktury

Prefix ? před názvem vnořeného pole potlačí chybu pokud sloupce neexistují (LEFT JOIN kde může být NULL):

$nested = Database::createNestedStructure($rows, [
    'users_id',
    'email',
    '?profile' => [        // nepovinné — nevyhodí chybu pokud sloupce chybí
        'profile_id',
        'bio'
    ]
]);

Přes QueryBuilder

Nested structure lze definovat přímo v QueryBuilderu:

$users = UsersRepository::queryBuilder()
    ->join('LEFT JOIN orders ON orders.user_id = users.id')
    ->setColumns(['users.id as users_id', 'users.email', 'orders.id as orders_id', 'orders.product_id'])
    ->createNestedStructure([
        'users_id',
        'email',
        'orders' => [
            'orders_id',
            'product_id'
        ]
    ])
    ->getResult();

// getSingleResult() vrátí jeden vnořený záznam
$user = UsersRepository::queryBuilder()
    ->criterion([['[users].[id] = ?', $userId]])
    ->join('LEFT JOIN orders ON orders.user_id = users.id')
    ->createNestedStructure([...])
    ->getSingleResult();

Přejmenování sloupců

Sloupce lze přejmenovat pomocí string klíče — klíč je název sloupce z DB, hodnota je název ve výsledku:

$nested = Database::createNestedStructure($rows, [
    'users_id',
    'email' => 'userEmail',     // users.email → userEmail ve výsledku
    'orders' => [
        'orders_id',
        'product_id' => 'pid'   // orders.product_id → pid ve výsledku
    ]
]);

generateUniqueKey

Metoda Database::generateUniqueKey() generuje unikátní slug/klíč pro danou tabulku. Pokud hodnota již existuje, automaticky přidá číslo na konec.

use Moony\bootstrap\core\helpers\Database;

// Automatické číslování: "my-article", "my-article2", "my-article3"
$slug = Database::generateUniqueKey(
    Str::slug($title),     // hodnota
    'articles',             // tabulka
    'slug'                  // sloupec
);

// Vlastní callback pro formát
$slug = Database::generateUniqueKey(
    Str::slug($title),
    'articles',
    'slug',
    static function(string $original, int $iteration) {
        return $original . '-' . $iteration;  // "my-article-2", "my-article-3"
    }
);
$valueVýchozí hodnota (slug, klíč)
$tableTabulka v DB
$columnSloupec který musí být unikátní
$callbackVolitelný — dostane originál + číslo iterace, vrátí novou hodnotu k ověření