1

I'm currently using migrations to change/swap content. On certain entries, there are assigned category fields attached to specific entry types. I am consolidating all of these to a single category field and also changing the entry types to the default. The new field is on another fieldlayout since it's a different entry type. I need a way to save the old category information in the short term so I can assign it.

What's happening is sometimes it can find the field, sometimes it can't:

public function safeUp()
    {

        $sectionFieldMappings = [
            [
                'sectionHandle' => 'bowlerProducts',
                'entryTypeHandle' => 'product',
                'fieldHandle' => 'bowlerProductAttributes',
                'categoryHandle' => 'bowlerProductAttributes'
            ],
            [
                'sectionHandle' => 'centerProducts',
                'entryTypeHandle' => 'product',
                'fieldHandle' => 'bowlingCenterProductAttributes',
                'categoryHandle' => 'bowlingCenterProductAttributes'
            ]
        ];


        foreach ($sectionFieldMappings as $sectionFieldMapping) {

            $sectionHandle = $sectionFieldMapping['sectionHandle'];
            $entryTypeHandle = $sectionFieldMapping['entryTypeHandle'];
            $attributesFieldHandle = $sectionFieldMapping['fieldHandle'];
            $categoryHandle = $sectionFieldMapping['categoryHandle'];

            $section = Craft::$app->sections->getSectionByHandle($sectionHandle);
            $entryTypes = $section->getEntryTypes();
            $defaultEntryType = null;

            foreach ($entryTypes as $entryType) {
                if ($entryType->handle == $entryTypeHandle) {
                    $defaultEntryType = $entryType;
                }
            }



            if ($defaultEntryType) {

                $entries = Entry::find()
                    ->section($sectionHandle)
                    ->anyStatus()
                    ->all();

                foreach ($entries as $entry) {

                    // need to make a note of the current category slug
                    // once we change entry type, it won't be accessible
                    $categoriesToSave = $this->getCategorySlugs($entry);

                    // change entry type and save entry so the new field is accessible
                    $entry->typeId = $defaultEntryType->id;
                    $entry->fieldLayoutId = $defaultEntryType->fieldLayoutId;

                    if (Craft::$app->elements->saveElement($entry)) {

                        echo $entry->title . " saved. \n";

                        $savedEntry = Craft::$app->getEntries()->getEntryById($entry->id);

                        $allCategoryIds = [];

                        foreach ($categoriesToSave as $categoryToSave) {
                            $categoryIds = $this->getCategoryIdsFromSlugs($categoryToSave['slugs'], $categoryToSave['parentTitle'], $categoryHandle);
                            $allCategoryIds = array_merge($allCategoryIds, $categoryIds);
                        }

                        echo "the entry fieldlayoutid is  " . $entry->fieldLayoutId . " and the product fieldLayout id is " . $defaultEntryType->fieldLayoutId;

                        try {
                            echo "setting field: " . $savedEntry->getFieldValue($attributesFieldHandle) . "\n";
                        } catch (Exception $e) {
                            echo "couldn't find field " . $attributesFieldHandle . "\n";
                        }

                        $savedEntry->setFieldValue($attributesFieldHandle, $allCategoryIds);
                        echo "All category IDs to assign: " . implode(",", $allCategoryIds) . "\n";

                        if (Craft::$app->elements->saveElement($savedEntry)) {
                            echo "Saved after setting field value \n";
                        } else {
                            echo "Failed to save after setting field value \n";
                        }
                    }
                }
            }
        }
    }

Above is code that changes the entry type to the default entry type, then on save success, use information from its previous fieldlayout and add that to the field found on the default entry type.

I've confirmed the the correct field layout ID with echo "the entry fieldlayoutid is " . $entry->fieldLayoutId . " and the product fieldLayout id is " . $defaultEntryType->fieldLayoutId; \n

and the existence of the field with:

try {
  echo "setting field: " . $savedEntry->getFieldValue($attributesFieldHandle) . "\n";
} catch (Exception $e) {
    echo "couldn't find field " . $attributesFieldHandle . "\n";
}

On first run craft migrate/up, the catch block is called because the field handle is invalid. If I let it run all the way, all the entries entryType is correctly swapped, however, they were not assigned category relations on the new field.

If I force quit the migration and then run craft migrate/up again, then it is able to find the field.

I've tried clearing/flushing all caches.

Each time I run a migration, I restore the previous database.

Edit:

It's possible the migration before this one that is responsible for creating a new field and adding it to the entry type is getting messed up. Still noting that it works in that it adds the new field/tab to the field layout I specified. but something internal to craft api doesn't seem to know it's there until other actions take place.

The migration is:

public function safeUp()
    {
        $fieldSectionMapping = [
            'centerProducts' => 'bowlingCenterProductAttributes',
            'bowlerProducts' => 'bowlerProductAttributes'
        ];

        foreach ($fieldSectionMapping as $sectionHandle => $fieldHandle) {

            $field = Craft::$app->fields->getFieldByHandle($fieldHandle);
            $section = Craft::$app->sections->getSectionByHandle($sectionHandle);
            $entryTypeHandle = "product";
            $defaultEntryType = null;

            foreach ($section->getEntryTypes() as $entryType) {
                if ($entryType->handle == $entryTypeHandle) {
                    $defaultEntryType = $entryType;
                }
            }

            $fieldLayout = $defaultEntryType->getFieldLayout();
            $layout = $entryType->getFieldLayout();
            $tabs = $layout->getTabs();

            foreach ($tabs[0]->elements as $i => $element) {
                if ($element instanceof CustomField && $element->getField()->handle === 'bowlingCenterProductAttributes') {
                    return true;
                }
            }

            $newFieldElement = new CustomField($field);

            $newTab = new FieldLayoutTab([
                'name' => 'Product Attributes',
                'sortOrder' => count($tabs) + 1,
            ]);
            $newTab->elements = [$newFieldElement];

            $tabs[] = $newTab;
            $fieldLayout->setTabs($tabs);

            Craft::$app->getFields()->saveLayout($fieldLayout);
        }
    }

What I did was comment this out completely, manually create the fields and assign them to the appropriate entry type layout, then run my migrations for the first time and then the field that was invalid before, is now found.

1 Answer 1

1

I'm not sure if this is too specific to help someone in the future, but maybe it will directly or indirectly.

After adding the field to a different entry type field layout, I added this:

foreach ($section->getEntryTypes() as $entryType) {
   Craft::$app->getSections()->saveEntryType($entryType);
}

Saving the actual entryType after saving the layout is what allowed me to access this.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.