statamic / ideas

đź’ˇDiscussions on ideas and feature requests for Statamic
https://statamic.dev
31 stars 1 forks source link

Get and set blueprint fields #261

Open aerni opened 4 years ago

aerni commented 4 years ago

I need to get a field from a blueprint by its handle, change some config values, and save it back to the blueprint. The following code is almost working. The only problem is, that I can't save the new config to the blueprint. The ensureField() method merges the config with the original one, thus the original values get preserved. I need it to be merged the other way around.

Maybe we can add a method parameter to ensureField() to change the merge behavior? Or add a separate method for this task?

// Get the blueprint and the field
$blueprint = Blueprint::find("collections/products/product");
$field = $blueprint->field('categories');

// Get the field config and set the taxonomy handle to a new value
$config = $field->config();
$config['taxonomy'] = $this->categories;

// Save the new config to a variable
$categoriesField = $field->setConfig($config)->config();

// Add the field to the blueprint and save it.
$blueprint->ensureField('categories', $categoriesField)->save();

I would also like to change the field handle as well, but couldn't find a way to do this. As soon as you change the handle, the field will be treated as a new field, thus the old field remains in the blueprint. A method to swap a whole field with a new one would be great. There's a removeField() method I could use to remove the old field. But the new field won't be placed in the exact same position as the old one. And that's a problem

jasonvarga commented 4 years ago

Can you explain the use case for needing/wanting to do this?

aerni commented 4 years ago

Sure thing. It's for my Snipcart addon.

I have a setup command that creates a collection of products, taxonomies for categories and taxes, and the blueprints. This command runs automatically after installing the addon. So far so good.

I want to give the user the freedom to define their own additional fields in the blueprint of the products or to move stuff around according to their taste.

In the blueprint of the products, I also have two fields of type: taxonomy. One for categories and one for taxes. In the addon's config, the user can change the handles of the taxonomies to whatever they want:

'taxonomies' => [
    'categories' => 'categories',
    'taxes' => 'taxes',
],

If the user changes a handle, he has to rerun the setup command, which will create the new taxonomies and update the products collection. It will also update the handles of the previously mentioned taxonomy fields in the product blueprint.

This is how I update the blueprint:

protected function updateProductBlueprint(): void
{
    $productBlueprint = StatamicBlueprint::find("collections/{$this->products}/product");

    $content = $productBlueprint->contents();

    $content['sections']['basic']['fields'][5]['handle'] = $this->categories;
    $content['sections']['basic']['fields'][5]['field']['taxonomy'] = $this->categories;

    $content['sections']['advanced']['fields'][10]['handle'] = $this->taxes;
    $content['sections']['advanced']['fields'][10]['field']['taxonomy'] = $this->taxes;

    $productBlueprint->setContents($content)->save();
}

This works fine if the user never touched the product blueprint before. But if he did, this potentially messes up everything as the fields might not be at the exact key defined in the method.

I'm looking for a good solution to "search" for the handle and taxonomy in the blueprint and override the old handles with the new ones.

$this->ensureField() looks promising. But it merges the wrong way around. I want the new keys and values to take precedence over the old ones. We could either give that method a new parameter option to do that or create a new method altogether.

This is my prototype of a potential solution. Only thing it can't do yet is to also override the fields handle and not just the fields config.

I hope this is clear. Ask away if not.