Jump to content
LostKobrakai

Migrations

Recommended Posts

Yesterday I released 0.3.1 with the access migrations like described here:

It does also include a fix to allow usage in multisite setups using $config->paths->site instead of hardcoding /site/….

 

  • Like 2

Share this post


Link to post
Share on other sites

I'm making extensive use of migrations for a project and had a question regarding writing migrations for matrix fields.

When creating a new FieldtypeRepeaterMatrix field, I usually need to create two migrations. The first actually creates the blank matrix field, and then the second populates it with types/fields. This is because the template associated with the matrix is not actually created when the initial save() method is called when creating the field. Does anyone know if it is possible to force PW to create this template via the API?

Wanted to also say how useful this module is. Would love to see migrations built into the core one day as it's totally changed how we use PW. I've been thinking about submitting a pull request with some new migration classes I've been building, would be great to have some reusable templates for complex situations like this.

EDIT: I just realized I could probably just create the template as I would any other template on the initial field setup. But if there is an alternative save() method that would let PW handle the template creation that would still seem easier!

  • Like 2

Share this post


Link to post
Share on other sites

Replying to myself here in case anyone else was looking for a similar migration. This seems to work for setting up repeater fields via a single migration file. Not sure if the method I've used for generating the repeater template is sensible but it will do for now:

 

<?php

class Migration_2017_10_01_10_10_10 extends FieldMigration {

	public static $description = "<b>+FIELD</b> Custom Matrix (custom_matrix)";

	protected function getFieldName(){ return 'custom_matrix'; }

	protected function getFieldType(){ return 'FieldtypeRepeaterMatrix'; }

	protected function getMatrixTemplateName() {
		return FieldtypeRepeater::templateNamePrefix . $this->getFieldName();
	}

	protected function getMatrixTypes() {
		return [
			[
				'name' => 'foo',
				'label' => 'Foo',
				'head' => '{matrix_label}',
				'fields' => [
					'title' => [
						'label' => 'Title (Optional)',
						'required' => 0,
					],
					'markdown',
				],
			], [
				'name' => 'bar',
				'label' => 'Bar',
				'head' => '{matrix_label}',
				'fields' => [
					'title' => [
						'label' => 'Title (Required)',
						'required' => 0,
					],
					'image',
				],
			],
		];
	}

	protected function addFieldToRepeaterList($matrixField, $field) {
		$repeaterFields = $matrixField->repeaterFields;
		if (!is_array($repeaterFields)) $repeaterFields = [];
		if (!in_array($field->id, $repeaterFields)) {
			$repeaterFields[] = $field->id;
		}
		$matrixField->repeaterFields = $repeaterFields;
	}

	protected function addMatrixType($matrixField, $index, $config) {
		$templateName = $this->getMatrixTemplateName();
		$prefix = 'matrix' . ($index + 1) . '_';

		$matrixField[$prefix.'name'] = $config['name'];
		$matrixField[$prefix.'label'] = $config['label'];
		$matrixField[$prefix.'head'] = $config['head'];
		$matrixField[$prefix.'sort'] = $index;

		$fields = [];
		foreach ($config['fields'] as $key => $property) {
			$field = (!is_numeric($key))
				? $this->fields->get($key)
				: $this->fields->get($property);

			$fields[] = $field->id;
			$this->addFieldToRepeaterList($matrixField, $field);
			$this->insertIntoTemplate($templateName, $field);

			if (!is_numeric($key)) {
				$this->editInTemplateContext($templateName, $field, function(Field $f) use ($property) {
					foreach ($property as $prop => $val) {
						$f->$prop = $val;
					}
				});
			}
		}

		$matrixField[$prefix.'fields'] = $fields;
	}

	protected function fieldSetup(Field $f){
		$f->label = 'Custom Matrix';
		$f->tags = 'matrix';
		$f->required = 1;
		$f->icon = 'fa-bars';

		// Do this to generate a template file for the repeater
		// @TODO Find a better way to do this
		$fieldtype = $f->type;
		$numOldReady = $fieldtype->countOldReadyPages($f);

		// Setup matrix stuff
		$matrixTypes = $this->getMatrixTypes();
		foreach ($matrixTypes as $i => $matrixData) {
			$this->addMatrixType($f, $i, $matrixData);
		}

    // Insert into template
    $template = 'my-template';
		$this->insertIntoTemplate($template, $f);
	}

}

 

  • Like 1

Share this post


Link to post
Share on other sites

Was about to ask about FiledtypeTable migrations but decided to try myself and it seems to work fine this way, dropping it here in case someone's in need:

 

<?php

class Migration_2017_10_23_12_24_20 extends FieldMigration {

	public static $description = "Add table field";

	protected function getFieldName(){ return 'booking_advanced_fields'; }

	protected function getFieldType(){ return 'Table'; }

	protected function fieldSetup(Field $f){
		$f->label = 'Booking Form Extra Fields';
		$f->collapsed = Inputfield::collapsedNever;
        $this->insertIntoTemplate('home', $f, "booking_advanced", true);
		//Booking advanced is a checkbox in this case, hence the showIf setup to make the table appear if required
        $this->editInTemplateContext('home', $f, function(Field $f) {
            $f->set("showIf","booking_advanced=1");
        });
        $f->set("maxCols", 4);
        
        $f->set("col1name", "column1");
        $f->set("maxCols", 4);
        $f->set("col1type", "text");
        $f->set("col1sort", "1");
        $f->set("col1label", "Column 1 Label");
        $f->set("col1width", 50);
        $f->set("col1settings", "textformatters=TextformatterEntities\nplaceholder=\ndefault=\nmaxLength=2048" );
        
        $f->set("col2name", "column2");
        $f->set("col2type", "text");
        $f->set("col2sort", "2");
        $f->set("col2label", "Column 2 Label");
        $f->set("col2width", 50);
        $f->set("col2settings", "textformatters=TextformatterEntities\nplaceholder=\ndefault=\nmaxLength=2048" );
        $f->save();

        $table_module = wire("modules")->get("FieldtypeTable");

        $table_module->_checkSchema($f, true);
        
	}

}

 

  • Like 2

Share this post


Link to post
Share on other sites

Hello! Thanks for the wonderful module!

Does anyone know, how to use it for FieldtypeOptions? Like picking the type (single or multiple) and setting options values. My attempts for this have failed so far.

@LostKobrakai

Edited by simonsays

Share this post


Link to post
Share on other sites

@simonsays Something like that:

class Migration_xxxx_xx_xx_xx_xx_xx extends FieldMigration {

	public static $description = "Create field";

	protected function getFieldName(){ return 'field_name'; }

	protected function getFieldType(){ return 'FieldtypeOptions'; }

	protected function fieldSetup(Field $f){
		$f->label = 'Label';
		$f->inputfieldClass = 'InputfieldRadios';
		$options = <<< _END
34
36
38
40
42
44
46
48
_END;
		$f->type->manager->setOptionsString($f, $options, true);
	}
}

 

  • Like 2

Share this post


Link to post
Share on other sites
1 hour ago, LostKobrakai said:

@simonsays Something like that:


class Migration_xxxx_xx_xx_xx_xx_xx extends FieldMigration {

	public static $description = "Create field";

	protected function getFieldName(){ return 'field_name'; }

	protected function getFieldType(){ return 'FieldtypeOptions'; }

	protected function fieldSetup(Field $f){
		$f->label = 'Label';
		$f->inputfieldClass = 'InputfieldRadios';
		$options = <<< _END
34
36
38
40
42
44
46
48
_END;
		$f->type->manager->setOptionsString($f, $options, true);
	}
}

 

Awesome, thanks @LostKobrakai! Saved me a lot of effort and I also became slightly smarter in the process ;)

Share this post


Link to post
Share on other sites

@LostKobrakai

When I try to install module using your migration, extra permission declared for this module in getModuleInfo() is not installed automatically.

When I install module via admin, permission is added OK.

Is this a bug? Or was it done like that intentionally?

Edited by simonsays

Share this post


Link to post
Share on other sites

It's certainly not intentional.

https://github.com/LostKobrakai/Migrations/blob/master/classes/ModuleMigration.php

There's what a module migration does. I'd expect that $this->modules->install($name) would also take care about installing any permissions of modules. — I just quickly checked the source and it seems this method should handle that. 

  • Like 2

Share this post


Link to post
Share on other sites
On 5/14/2018 at 5:57 PM, LostKobrakai said:

It's certainly not intentional.

https://github.com/LostKobrakai/Migrations/blob/master/classes/ModuleMigration.php

There's what a module migration does. I'd expect that $this->modules->install($name) would also take care about installing any permissions of modules. — I just quickly checked the source and it seems this method should handle that. 

Weird, failed first 2 times, but works perfectly now. Logs did not find anything. Guess, something with my localhost...

Share this post


Link to post
Share on other sites

@LostKobrakai

Do you happen to have a good example of adding a repeater field (just the field, no data)?

Found this old thread, which introduces a fairly complex approach, but it is rather old, so I was hoping, that something has changed over the last 4 years.

Looked through FieldtypeRepeater module, but did not find any helpful methods.

Share this post


Link to post
Share on other sites

@LostKobrakai

I have this code for repeater migration, however, I noticed something. Not sure if it is a bug or the way it should be.

<?php

class Migration_2018_11_18_00_00_00_RepeaterAccordionField extends FieldMigration {

    public static $description = "Add repeater for accordion";

    protected function getFieldName() {
        return 'repeater_accordion';
    }

    protected function getFieldType() {
        return 'FieldtypeRepeater';
    }

    protected function fieldSetup(Field $f) {

        $f->label = 'Repeater accordion';
        $f->collapsed = Inputfield::collapsedNever;

        $repeaterFieldGroup = new Fieldgroup();
        $repeaterFieldGroup->name = 'repeater_' . $this->getFieldName();

        //Add fields to fieldgroup - add others as necessary
        $repeaterFieldGroup->append($this->fields->get('title'));
        $repeaterFieldGroup->append($this->fields->get('content_columns'));

        $repeaterFieldGroup->save();

        $repeaterTemplate = new Template();
        $repeaterTemplate->name = 'repeater_' . $this->getFieldName();
        $repeaterTemplate->flags = 8;
        $repeaterTemplate->noChildren = 1;
        $repeaterTemplate->noParents = 1;
        $repeaterTemplate->noGlobal = 1;
        $repeaterTemplate->slashUrls = 1;
        $repeaterTemplate->fieldgroup = $repeaterFieldGroup;

        $repeaterTemplate->save();

        $repeaterPage = "for-field-{$f->id}";
        $f->parent_id = $this->pages->get("name=$repeaterPage")->id;
        $f->template_id = $repeaterTemplate->id;
        $f->repeaterReadyItems = 3;
        
        //Add fields to the repeater - add others as necessary
        $f->repeaterFields = $this->fields->get('title');
        $f->repeaterFields = $this->fields->get('content_columns');

        $f->save();
    }

}

If I rollback the migration, it deletes the field. But does not affect the fieldgroup.

So, when I try to run the migration for the second time - it throws me an integrity violation error (originates from fieldgroups table).

Is this the expected behaviour?

Edited by simonsays
specify

Share this post


Link to post
Share on other sites

You can look into the FieldMigration class to see what it does on migration/rollback. It should just remove the field itself. If that doesn‘t clean up the fieldgroup than it‘s expected behaviour. You can still override/add to that by putting your own upgrade/downgrade functions like e.g. I did it here: https://github.com/LostKobrakai/MigrationSnippets/blob/master/Reverse_Template_Migration_Type.php

Share this post


Link to post
Share on other sites
16 hours ago, LostKobrakai said:

You can look into the FieldMigration class to see what it does on migration/rollback. It should just remove the field itself. If that doesn‘t clean up the fieldgroup than it‘s expected behaviour. You can still override/add to that by putting your own upgrade/downgrade functions like e.g. I did it here: https://github.com/LostKobrakai/MigrationSnippets/blob/master/Reverse_Template_Migration_Type.php

It was just a quick question to confirm, whether repeater field downgrade works out of the box or requires additional extending.

Thanks for the reply.

Share this post


Link to post
Share on other sites

@LostKobrakai

Sorry for noob question, but after installing Migrations module and creating my first migration, my IDE gives an error because the variable $this is unexpected...

image.png.fcf59fbff37fb2b5ce01fd6f3e5da8de.png

It seems like no one else has encounter this...is something wrong with my pw setup? Any hints on where I can look for answers?

Share this post


Link to post
Share on other sites

@rastographics, it looks like you're missing a semicolon (";") at the end of the line starting with "$f->description". Or was the screenshot cut off early? 🙂

  • Like 1

Share this post


Link to post
Share on other sites

As hinted in the topic on RockMigrations I'll deprecate my module in favor of it. I've added noted to github and this topic:

Quote

This module is deprecated in favor of RockMigrations. It'll continue to work and I might fix some smaller incompatibilities if they're reported, but no major development will happen on this anymore.

 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...