0

I'm writing unit tests for a plugin that reacts to adding fields to field layouts, I'm trying to add fields to a layout programmatically but can't get anywhere,

In the example below (method testAddingFieldToEntryType) after the entry type is saved with amended layout
the layout that comes from $entryType->fieldLayout and
the layout from \Craft::$app->sections->getEntryTypeById($entryType->id)->fieldLayout)
are different objects, and the latter is empty no matter what I try. The database looks correct though, I can see the layout, fields and tabs created.

I also tried the solution posted there : Copy the Entry Type along with its field layout with the exact same result.

Any idea what I'm doing wrong ?

Thank you for your time

Steps to reproduce

  1. Unit test :
class Test extends Unit
{
    protected $tester;

    public function _fixtures()
    {
        return [
            'sections' => SectionsFixture::class,
            'fields' => FieldsFixture::class
        ];
    }
    public function testAddingFieldToEntryType()
    {
        $sectionsFixture = $this->tester->grabFixture('sections');
        $fieldsFixture = $this->tester->grabFixture('fields');
        $section = $sectionsFixture->getSection(0);
        $type = $section->entryTypes[0];

        $assetField = $fieldsFixture->getField('assets');
        $sectionsFixture->addFieldToEntryType($assetField, $type);

         //The following seems correct, and consistent with the database, the layout/tabs ids are not set though :
        dump($entryType->fieldLayout); 
        //This has a correct id (looking at database) and uid, but is empty otherwise :
        dd(\Craft::$app->sections->getEntryTypeById($entryType->id)->fieldLayout);
    }
}

  1. Fixtures :
class SectionsFixture extends DbFixture
{
    use FileFixtureTrait;
    use DbFixtureTrait;

    public $dataFile = __DIR__ . '/data/sections.php';

    private $_sections = [];

    public function load()
    {
        codecept_debug('Loading sections');
        foreach ($this->loadData($this->dataFile) as $key => $data) {
            $section = $this->createSection($data);

            if (!$this->saveSection($section)) {
                throw new \Exception(implode(' ', $section->getErrorSummary(true)));
            }

            $this->_sections[$key] = $section;
        }
    }

    public function unload()
    {
        codecept_debug('Unloading sections');
        foreach ($this->_sections as $key => $section) {
            $this->deleteSection($key);
        }
        $this->hardDelete();
    }

    public function deleteSection($key)
    {
        if (isset($this->_sections[$key])) {
            $section = $this->_sections[$key];
            unset($this->_sections[$key]);
            return \Craft::$app->sections->deleteSection($section);
        }
    }

    public function getSection(string $key): ?Section
    {
        return $this->_sections[$key] ?? null;
    }

    public function addFieldToEntryType(Field $field, EntryType $entryType)
    {
        $layout = $entryType->fieldLayout;
        $tab = $layout->tabs[0];
        $fields = $layout->fields;
        $fields[] = $field;
        $layout->fields = $fields;
        $tab->elements[] = \Craft::$app->fields->createLayoutElement([
            'type' => CustomField::class,
            'fieldUid' => $field->uid
        ]);
        if (!\Craft::$app->sections->saveEntryType($entryType)) {
            throw new \Exception(implode(' ', $entryType->getErrorSummary(true)));
        }
    }

    protected function createSection(array $data): Section
    {
        $allSiteSettings = [];
        foreach (\Craft::$app->getSites()->getAllSites() as $site) {
            $siteSettings = new Section_SiteSettings();
            $siteSettings->siteId = $site->id;
            $allSiteSettings[$site->id] = $siteSettings;
        }
        $data['siteSettings'] = $allSiteSettings;
        return new Section($data);
    }

    protected function saveSection(Section $section)
    {
        return \Craft::$app->sections->saveSection($section);
    }
}
class FieldsFixture extends DbFixture
{
    use FileFixtureTrait;
    use DbFixtureTrait;

    public $dataFile = __DIR__ . '/data/fields.php';

    private $_fields = [];

    public function load()
    {
        codecept_debug('Loading fields');
        foreach ($this->loadData($this->dataFile) as $key => $data) {
            $field = $this->createField($data);

            if (!$this->saveField($field)) {
                throw new \Exception(implode(' ', $field->getErrorSummary(true)));
            }

            $this->_fields[$key] = $field;
        }
    }

    public function unload()
    {
        codecept_debug('Unloading fields');
        foreach ($this->_fields as $key => $field) {
            $this->deleteField($key);
        }
        $this->hardDelete();
    }

    public function deleteField($key)
    {
        if (isset($this->_fields[$key])) {
            $field = $this->_fields[$key];
            unset($this->_fields[$key]);
            return \Craft::$app->fields->deleteField($field);
        }
    }

    public function getField($key): ?Field
    {
        return $this->_fields[$key] ?? null;
    }

    protected function createField(array $data): Field
    {
        $class = $data['type'];
        return new $class($data['data']);
    }

    protected function saveField(Field $field)
    {
        return \Craft::$app->fields->saveField($field);
    }
}
  1. Data : data/sections.php
return [
    [
        'handle' => 'myChannel',
        'name' => 'My Channel',
        'type' => Section::TYPE_CHANNEL
    ],
    [
        'handle' => 'myStructure',
        'name' => 'My Structure',
        'type' => Section::TYPE_STRUCTURE
    ]
];

data/fields.php

return [
    'assets' => [
        'type' => Assets::class,
        'data' => [
            'handle' => 'assets',
            'name' => 'Assets'
        ]
    ]
];
  1. Run the test

Additional info

  • Craft version: 3.6.18
  • PHP version: 7.4
  • Database driver & version: mysql 5.7

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.