Model » Nested models

Mondoc supports nested models, which are models that are contained within another model. This feature is useful when you need to represent complex data in your MongoDB documents.

Nesting objects by using the MondocAbstractSubModel

The MondocAbstractSubModel is actually the base class for all models in Mondoc. It ultimately provides the ability to inflate and deflate models into a suitable format for MongoDB.

Did you know: Nested models also support nested models. This means you can have a nested model which contains a nested model, which contains a nested model, and so on, and so forth.

Nested models can be either a singular model or an array of models. This is determined by the $mondocNested property in the top level model.

Consider the following example. We have a generic Person model, which does not extend the MondocAbstractModel class. Instead, it simply extends the MondocAbstractSubModel class.

<?php

use District5\Mondoc\Db\Model\MondocAbstractSubModel;

class Person extends MondocAbstractSubModel
{
    protected string|null $name = null;

    public function getName(): string|null
    {
        return $this->name;
    }

    public function setName(string $name): void
    {
        $this->name = $name;
    }
}

To use the above Person model as a nested model in a top level model (e.g. CompanyModel), you need to define the nested model in the top level model. The $mondocNested property is used to declare this relationship.

<?php

use District5\Mondoc\Db\Model\MondocAbstractModel;
use Person;

class CompanyModel extends MondocAbstractModel
{
    protected Person|null $boss = null; // Single Person model

    protected array $people = []; // Array of Person models

    protected array $mondocNested = [
        'boss' => Person::class, // Single nested model
        'people' => Person::class . '[]' // Array of nested models
    ];

    public function getBoss(): Person|null
    {
        return $this->boss;
    }

    public function setBoss(Person $boss): void
    {
        $this->boss = $boss;
    }

    public function getPeople(): array
    {
        return $this->people;
    }

    public function setPeople(array $people): void
    {
        $this->people = $people;
    }
}

Flexible nested models

The need may arise for you to handle different models within the same field, for example in the case of an eCommerce store. MondocAbstractSubModel supports this by overriding the getMondocNestedModelMap method.

Please note:

This feature has been supported since 6.6.0.
Implementing the getMondocNestedModelMap method is optional, but overrides the requirement of the $mondocNested property entirely, therefore you must not use both.

Take the following nested models as an example, depending on your chosen provider, you can easily control the nested model class that is used.

Despite the following being against a single model, you can still alter these easily to become multiple models, by appending [] to the defined class name.

<?php
namespace My\Models\Gateways;

use District5\Mondoc\Db\Model\MondocAbstractSubModel;

class Stripe extends MondocAbstractSubModel
{
    // Stripe specific fields
}
<?php
namespace My\Models\Gateways;

use District5\Mondoc\Db\Model\MondocAbstractSubModel;

class Worldpay extends MondocAbstractSubModel
{
    // Worldpay specific fields
}

Flexibility means you can define the nested model class at runtime, which is useful when you have different model providers for different environments.

<?php
namespace My\Models;

use District5\Mondoc\Db\Model\MondocAbstractModel;

use My\Models\Gateways\Stripe;
use My\Models\Gateways\Worldpay;

class CustomerPayment extends MondocAbstractModel
{
    protected Stripe|Worldpay|null $paymentGateway = null;

    /**
     * @return array
     */
    public function getMondocNestedModelMap(): array
    {
        return [
            'paymentGateway' => getenv('PAYMENT_GATEWAY') === 'stripe' ? Stripe::class : Worldpay::class
        ];
    }
}