Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.x]: Unable to programmatically create a custom field and assign to Product. #3246

Open
msbit opened this issue Aug 10, 2023 · 2 comments
Open
Assignees
Labels
ℹ️ status: need more info When waiting for user to supply database or more information.

Comments

@msbit
Copy link
Contributor

msbit commented Aug 10, 2023

What happened?

Description

I am unable to programmatically create a custom field, and subsequently assign it to a Product. It works for User, and for Order, but not for Product. I suspect it's related to Product delegating to ProductType for its field layout under certain circumstances, but I can't work it out. I'd be happy to be told how I'm holding this wrong 🤣

Cross post

Steps to reproduce

From a fresh install of Craft + Commerce, under php craft shell, require('foo.php'); where foo.php is:

<?php

use craft\fields\Number;
use craft\models\FieldLayout;
use craft\commerce\elements\Product;
use craft\commerce\models\ProductType;
use craft\commerce\models\ProductTypeSite;

function random()
{
    return array_reduce(range(0, 9), fn ($x) => $x . range('a', 'z')[rand(0, 25)], '');
}

$salt = random();

$result = Craft::$app->getFields()->saveField($field = new Number([
    'handle' => $salt,
    'name' => $salt,
]));
assert($result === true);
assert($field->hasErrors() === false);

$result = Craft::$app->getFields()->saveLayout($layout = new FieldLayout([
    'fields' => [$field],
    'type' => Product::class,
]));
assert($result === true);
assert($layout->hasErrors() === false);

$result = craft\commerce\Plugin::getInstance()->getProductTypes()->saveProductType($productType = new ProductType([
    'handle' => $salt,
    'hasDimensions' => false,
    'hasVariants' => false,
    'name' => $salt,
    'siteSettings' => [
        Craft::$app->getSites()->getCurrentSite()->id => new ProductTypeSite(),
    ],
]));
assert($result === true);
assert($productType->hasErrors() === false);

$result = Craft::$app->getElements()->saveElement($product = new Product([
    'slug' => $salt,
    'title' => $salt,
    'typeId' => $productType->id,
    'variants' => [
        [
            'hasUnlimitedStock' => false,
            'price' => 0,
            'maxQty' => 0,
            'minQty' => 0,
        ],
    ],
    $salt => 42,
]));
assert($result === true);
assert($product->hasErrors() === false);
assert($product->{$salt} === 42);
assert(Craft::$app->getElements()->getElementById($product->id, Product::class)->{$salt} === 42);

Expected behavior

All assertions to pass.

Actual behavior

WARNING  assert(): assert($product->$salt === 42) failed in foo.php on line 58.
WARNING  assert(): assert(Craft::$app->getElements()->getElementById($product->id, Product::class)->$salt === 42) failed in foo.php on line 59.

Craft CMS version

3.8.16

Craft Commerce version

3.4.22.1

PHP version

7.4.33

Operating system and version

macOS 13.4.1

Database type and version

mysql 8.0.34

Image driver and version

No response

Installed plugins and versions

@msbit msbit added commerce3 Issues related to Commerce v3 bug labels Aug 10, 2023
@lukeholder lukeholder self-assigned this Aug 21, 2023
@lukeholder
Copy link
Member

Could you please let us if this persists in Commerce 4 please? We will then take a look. Thanks!

@lukeholder lukeholder added ℹ️ status: need more info When waiting for user to supply database or more information. and removed bug commerce3 Issues related to Commerce v3 labels Nov 20, 2023
@msbit
Copy link
Contributor Author

msbit commented Nov 20, 2023

Hey @lukeholder, I'm not going to be able to run this on 4.x for a while, but I've modified my test code to the following:

function createFieldLayout(string $type, Field ...$fields): FieldLayout
{
    Craft::$app->getFields()->saveLayout($layout = new FieldLayout([
        'fields' => $fields,
        'tabs' => [
            [   
                'fields' => $fields,
                'name' => $type,
            ],  
        ],  
        'type' => $type,
    ]));
    return $layout;
}
function createProductType(string $handle, string $name = null): ProductType
{
    CommercePlugin::getInstance()->getProductTypes()->saveProductType($productType = new ProductType([
        'fieldLayoutId' => Craft::$app->getFields()->getLayoutByType(Product::class)->id,
        'handle' => $handle,
        'hasDimensions' => false,
        'hasVariants' => true,
        'name' => $name ?? $handle,
        'siteSettings' => [
            Craft::$app->getSites()->getPrimarySite()->id => new ProductTypeSite(),
        ],  
        'variantFieldLayoutId' => Craft::$app->getFields()->getLayoutByType(\craft\commerce\elements\Variant::class)->id,
    ]));
    return $productType;
}

and

function createProduct(ProductType $type, string $slug, array $attributes = [], array $variantAttributes = []): Product
{
    Craft::$app->getElements()->saveElement($product = new Product(array_merge([
        'slug' => $slug,
        'title' => $slug,
        'typeId' => $type->id,
        'variants' => [
            array_merge([
                'price' => 1,
                'stock' => 1,
                'title' => $slug,
            ], $variantAttributes),
        ],  
    ], $attributes)));
    return $product;
}

Running something like:

    createFieldLayout(
        \craft\commerce\elements\Product::class,
        // some fields
    );  

    createFieldLayout(
        \craft\commerce\elements\Variant::class,
        // some fields
    );

then:

createProduct(
    createProductType("YQu4TZ4M9n6JzqkAGJMLq"),
    "8FX3-dEE2TwnZSfNLgPGd",
);

gets me a properly set up Product with the appropriate fields. I'm guessing the pertinent change was providing the:

  • fieldLayoutId, and
  • variantFieldLayoutId

explicitly when creating the ProductType?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ℹ️ status: need more info When waiting for user to supply database or more information.
Projects
None yet
Development

No branches or pull requests

2 participants