notta - 8 months ago

Hi, for the starters i am totally new to Pyro ans its community so this is also my hello post! :) I have been learning and researching it for only few days now, i have watched some guides, mainly module and theme development and im a bit stuck. I'm coming from the WP world and i have wanted to switch something reasonable for some time now and Pyro seems currently most developer friendly from Laravel CMS-ses. I have this small project that would consist of templates and custom fields and few general site options when talking about WP. Now from what i understand so far in Pyro Page Types are like templates and Fields are like custom fields that you can hook up with Page Types. The problem is that i have not found a way to create these page fields and page types programmatically other than literally instatiating Field and Page Type models and saving its entries into database. For all my projects in WP this kind of configurations have always been in code where i specify the templates i'm using and what kind of fields will be binded with those templates. I'm not finding anything related to creating fields and types only from code or am i missing something here?

piterden - 8 months ago

You can use migrations and seeders for creating types and fields programmatically. Join Slack and I will show you there.

ryanthompson - 8 months ago

Welcome @notta ! And @piterden any chance of a link or dropping that code here for others?

notta - 8 months ago

I ended up created something for myself that lets me define fields and where they show up. Note that it might have some mistakes as im not fully familiar with pyro yet but for starters i found it to be quite helpful for me to change fields more efficiently when i need to without losing any data if it is compatible with previous field (for example changing text field to wysiwyg field would keep the content and vice versa). I also hate defining fields and types in admin so that was my main reason for creating this. Of course it needs more work to be more efficient but for starters it's something.

use Illuminate\Console\Command;

class RunConfiguration extends Command
{

    /**
     * The console command name.
     *
     * @var string
     */
    protected $name = 'theme:configure';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Configures the theme related stuff.';

    /**
     * Execute the console command.
     *
     * @throws \Exception
     */
    public function handle()
    {

        dispatch(new ConfigureThemeHandler(app()->make(ConfigureTheme::class)));
    }

}
use Anomaly\PagesModule\Type\TypeModel;
use Anomaly\Streams\Platform\Assignment\AssignmentModel;
use Anomaly\Streams\Platform\Field\FieldModel;
use Anomaly\Streams\Platform\Stream\StreamModel;

class ConfigureThemeHandler extends BaseConfiguration
{

    public function __construct($config)
    {
        $this->setConfig($config);
    }

    public function getTemplateConfig($config)
    {
        return array_merge([
            'name'            => '',
            'description'     => '',
            'handler'         => 'anomaly.extension.default_page_handler',
            'theme_layout'    => 'theme::layouts.default',
            'required'        => false,
            'searchable'      => false,
            'translatable'    => false,
            'layout'          => '',
            'template_fields' => [],
        ], $config);
    }

    public function createTemplate($templateSlug, $config)
    {
        $config = $this->getTemplateConfig($config);

        $type = TypeModel::whereSlug($templateSlug)->first();

        if (!$type) {
            $type = new TypeModel();
        }

        $type->name = $config['name'];
        $type->description = $config['description'];
        $type->slug = $templateSlug;
        $type->handler = $config['handler'];
        $type->theme_layout = $config['theme_layout'];
        $type->layout = $config['layout'];

        $type->save();

        $streamId = $type->getEntryStream()->getId();

        if (isset($config['fields']) && is_array($config['fields']) && !empty($config['fields'])) {

            foreach ($config['fields'] as $fieldSlug) {

                $fieldId = FieldModel::whereSlug($fieldSlug)->first()->id;

                $assignment = AssignmentModel::where(['stream_id' => $streamId, 'field_id' => $fieldId])->first();

                if (!$assignment) {
                    $assignment = new AssignmentModel();
                }

                $fieldConfig = $this->getFieldConfig('page', $fieldSlug, $this->pageFieldsConfig[$fieldSlug]);

                $assignment->stream_id = $streamId;
                $assignment->field_id = FieldModel::whereSlug($fieldSlug)->first()->id;

                $assignment->required = $fieldConfig['required'];
                $assignment->searchable = $fieldConfig['searchable'];
                $assignment->translatable = $fieldConfig['translatable'];

                $assignment->save();

            }
        }
    }

    public function getStreamConfig($namespace, $repeaterSlug, $config)
    {
        return array_merge([
            'namespace'    => $namespace,
            'slug'         => $repeaterSlug,
            'prefix'       => 'repeater_',
            'title_column' => 'id',
            'order_by'     => 'id',
            'locked'       => 0,
            'hidden'       => 0,
            'sortable'     => 0,
            'trashable'    => 0,
            'translatable' => 0,
            'config'       => '',
        ], $config);
    }

    public function createStream($namespace, $repeaterSlug, $config)
    {
        $config = $this->getStreamConfig($namespace, $repeaterSlug, $config);

        $stream = StreamModel::where(['namespace' => $namespace, 'slug' => $repeaterSlug])->first();

        if (!$stream) {
            $stream = new StreamModel();
        }

        $stream->namespace = $namespace;
        $stream->slug = $repeaterSlug;
        $stream->prefix = $config['prefix'];
        $stream->title_column = $config['title_column'];
        $stream->order_by = $config['order_by'];
        $stream->locked = $config['locked'];
        $stream->hidden = $config['hidden'];
        $stream->sortable = $config['sortable'];
        $stream->trashable = $config['trashable'];
        $stream->translatable = $config['translatable'];
        $stream->config = $config['config'];

        $stream->save();

        $translationStream = ($stream->getTranslationModel())::whereStreamId($stream->id)->first();

        if ($translationStream) {

            $translationStream->name = $config['name'];
            $translationStream->description = $config['description'];

            $translationStream->save();
        }

        $streamId = $stream->getId();

        if (isset($config['fields']) && is_array($config['fields']) && !empty($config['fields'])) {

            foreach ($config['fields'] as $fieldSlug) {

                $fieldId = FieldModel::whereSlug($fieldSlug)->first()->id;

                $assignment = AssignmentModel::where(['stream_id' => $streamId, 'field_id' => $fieldId])->first();

                if (!$assignment) {
                    $assignment = new AssignmentModel();
                }

                $assignment->stream_id = $streamId;
                $assignment->field_id = FieldModel::whereSlug($fieldSlug)->first()->id;

                $assignment->save();
            }
        }
    }

    public function getFieldConfig($namespace, $fieldSlug, $config)
    {
        return array_merge([
            'namespace'    => $namespace,
            'slug'         => $fieldSlug,
            'type'         => 'anomaly.field_type.text',
            'locale'       => 'et',
            'name'         => '',
            'placeholder'  => '',
            'warning'      => '',
            'instructions' => '',
            'min'          => 0,
            'max'          => 0,
            'required'     => 0,
            'searchable'   => 0,
            'add_row'      => 'Add row',
        ], $config);
    }

    public function createFields($namespace, $fieldSlug, $config)
    {
        $config = $this->getFieldConfig($namespace, $fieldSlug, $config);

        $type = FieldModel::where(['namespace' => $namespace, 'slug' => $config['slug']])->first();

        if (!$type) {
            $type = new FieldModel();
        }

        $type->namespace = $namespace;
        $type->slug = $config['slug'];
        $type->type = $config['type'];

        if (isset($this->repeatersConfig[$fieldSlug])) {
            $type->config = [
                'related' => 'Anomaly\Streams\Platform\Model\Repeater\\' . 'Repeater' . camel_case($fieldSlug) . 'EntryModel',
                'min'     => $config['min'],
                'max'     => $config['max'],
                'add_row' => $config['add_row']
            ];
        }

        $type->save();

        $translationType = ($type->getTranslationModel())::whereFieldId($type->id)->first();

        if ($translationType) {

            $translationType->locale = $config['locale'];
            $translationType->name = $config['name'];
            $translationType->placeholder = $config['placeholder'];
            $translationType->warning = $config['warning'];
            $translationType->instructions = $config['instructions'];

            $translationType->save();
        }
    }

    function createRepeaters()
    {
        foreach ($this->repeaterFieldsConfig as $repeaterFieldSlug => $repeaterFieldConfig) {
            $this->createFields('repeater', $repeaterFieldSlug, $repeaterFieldConfig);
        }

        foreach ($this->repeatersConfig as $repeaterSlug => $repeaterConfig) {
            $this->createStream('repeater', $repeaterSlug, $repeaterConfig);
        }
    }

    function creeatePageFieldsAndTypes()
    {
        foreach ($this->pageFieldsConfig as $pageFieldSlug => $pageFieldConfig) {
            $this->createFields('pages', $pageFieldSlug, $pageFieldConfig);
        }

        foreach ($this->pageTypesConfig as $pageTypeSlug => $pageTypeConfig) {
            $this->createTemplate($pageTypeSlug, $pageTypeConfig);
        }
    }

    /**
     * Handle the command.
     *
     * @return bool
     */
    public function handle()
    {

        $this->createRepeaters();
        $this->creeatePageFieldsAndTypes();

        return true;
    }
}
class ConfigureTheme extends BaseConfiguration
{

    protected $repeaterFieldsConfig = [
        'my_repeter_field' => [
            'name'         => 'Repeater content',
            'type'         => 'anomaly.field_type.text',
            'translatable' => true
        ]
    ];

    protected $repeatersConfig = [
        'my_repeater' => [
            'name'         => 'Uhoooo',
            'description'  => 'heha on on',
            'fields'       => [
                'my_repeter_field'
            ],
            'translatable' => true
        ]
    ];

    protected $pageFieldsConfig = [
        'od_content'  => [
            'name'         => 'Small content',
            'type'         => 'anomaly.field_type.wysiwyg',
            'translatable' => true
        ],
        'my_repeater' => [
            'name'         => 'Repeater content',
            'type'         => 'anomaly.field_type.repeater',
            'translatable' => false
        ]
    ];

    protected $pageTypesConfig = [
        'od_frontpage'    => [
            'name'         => 'Frontpage Template',
            'theme_layout' => 'theme::pages.frontpage',
            'translatable' => false,
            'fields'       => [
                'od_content', 'my_repeater'
            ]
        ],
        'od_references'   => [
            'name'         => 'References Template',
            'theme_layout' => 'theme::pages.references',
        ],
        'od_services'     => [
            'name'         => 'Services Template',
            'theme_layout' => 'theme::pages.services',
        ],
        'od_achievements' => [
            'name'         => 'Achievements Template',
            'theme_layout' => 'theme::pages.achievements',
        ],
        'od_contact'      => [
            'name'         => 'Contact Template',
            'theme_layout' => 'theme::pages.contact',
        ],
        'od_certs'        => [
            'name'         => 'Certificates Template',
            'theme_layout' => 'theme::pages.certs',
        ]
    ];

    protected $adminEditPageConfig = [
        'tabs' => [
            'image_slider' => [
                'title'  => 'Image slider',
                'fields' => [
                    'entry_od_content',
                    'entry_my_repeater',
                ]
            ]
        ]
    ];
}

ryanthompson - 8 months ago

Nicely done!

Keep in mind too for changing fields you can use normal migrations (with up/down methods) and these helpers to find the actual model instances of them - and change things like name / type and it will indeed preserve data:

$field = $this->fields()->findBySlugAndNamespace($slug, $namespace);

$field->name = 'Foo Bar';
$field->sug = 'meh_slug';
$field->type = 'anomaly.field_type.textarea';

$field->save();

Your type is updated, schema is updated, streams are recompiled, etc.