Cms: Saving new entries programmatically

Created on 26 Jun 2017  路  12Comments  路  Source: craftcms/cms

Description

I've been trying for ages to figure out how to create and save a new Entry, via PHP with Craft 3.

I got as far as the following code:

$entry = new Entry();
$entry->sectionId = 4;
$entry->typeId = 4;
$entry->authorId = 1;
$id = uniqid('product_');
$entry->title = $id;
$entry->slug = $id;
$entry->setFieldValue('excerpt', 'Lorem...');
if(Craft::$app->elements->saveElement($entry)) {
    return $entry;
} else {
    throw new \Exception("Couldn't save new bespoke product: " . print_r($entry->getErrors(), true));
}

which saved without any errors, however my custom field values weren't being saved into the entry.

After lots of digging, I realised that it was because I was missing: $entry->fieldLayoutId = 10;.

I think that this can be improved in either of 2 ways:

  • A warning is thrown when saving entries with custom fields, that are missing a fieldLayoutId (or entries that are missing a layout id in general)
  • A helper method to save these entries, using the entry type handle, something like:
$newEntry = Craft::$app->entries->createNewEntry('product', [
    'title' => 'My Title',
    'slug' => 'my-title',
    'excerpt' => 'Lorem...',
    'images' => $imageIds,
]);

etc. The sectionId, typeId and fieldLayoutId could be found automatically based on the handle of the entry type.

Additional info

  • Craft version: 3.0.0-beta.19
  • PHP version: 7.1.6
  • Database driver & version: MySQL
  • Plugins & versions: N/A
enhancement

Most helpful comment

Starting in Beta 20, it will not be necessary to set $fieldLayoutId anymore. Thanks for the feedback!

All 12 comments

Yes, I've run into this.

Just fixed and blew through it as I was converting a big plugin, but truly matters would be much improved with a measage, something like 'one or more fields not saved, due to missing fieldLayoutId for them'

  • This should go in the log, naturally, but should also appear as a flash message.
  • in fact, shouldn't the save be denied if occuring on the front end, as really you did not get a save
  • in which case, really this should be an error message, more important than a warning?

Now that I've figured out what I'm doing, I created this:

public static function saveNewEntry(string $handle, array $fields) {
    $entryType = EntryType::find()->where(['handle' => $handle])->one();

    $entry = new Entry();
    $entry->sectionId = $entryType->getAttribute('sectionId');
    $entry->typeId = $entryType->getAttribute('id');
    $entry->fieldLayoutId = $entryType->getAttribute('fieldLayoutId');
    $entry->authorId = 1;

    if(isset($fields['title'])) {
        $entry->title = $fields['title'];
        unset($fields['title']);
    }

    if(isset($fields['slug'])) {
        $entry->slug = $fields['slug'];
        unset($fields['slug']);
    }

    $entry->setFieldValues($fields);

    if(Craft::$app->elements->saveElement($entry)) {
        return $entry;
    } else {
        throw new \Exception("Couldn't save new bespoke product: " . print_r($entry->getErrors(), true));
    }
}

This lets me save anything with:

return self::saveNewEntry('article', [
    'title' => $id,
    'slug' => $id,
    'excerpt' => $excerpt,
    'body' => $body
]);

I think you're right, that this case already shows up as a save error.

Not in a spot to check it at moment, though.

Which is showing as a save error? My latest code for saveNewEntry?

no, the fault with lacking fieldLayoutId that you were concerned with.

Ah ok. Yeah when I saved it without a fieldLayoutId, it didn't throw any errors but just silently continued.

Ok, then my original comment's suggestion stands....

Unexpected things shouldn't happen or non-happen silently.

Yeah sorry, just clearing things up. I read "This case already shows up as a save error." as it meaning it causes errors when saving.

Starting in Beta 20, it will not be necessary to set $fieldLayoutId anymore. Thanks for the feedback!

(Oh and a quick note - you don't need to set the $id property when creating a new entry. That will be assigned automatically on save.)

@SteveEdson

I got Call to undefined method craft\models\EntryType::find() error

Is the EntryType class still work in the following way?

use craft\models\EntryType;
EntryType::find()->where(['handle' => $handle])->one()

@max8hine You need to use the ActiveRecord class. The example below should work.

use craft\records\EntryType;
EntryType::find()->where(['handle' => $handle])->one();
Was this page helpful?
0 / 5 - 0 ratings

Related issues

angrybrad picture angrybrad  路  3Comments

lukebailey picture lukebailey  路  3Comments

angrybrad picture angrybrad  路  3Comments

michaelhue picture michaelhue  路  3Comments

angrybrad picture angrybrad  路  3Comments