Преобразование многомерного массива Dynami c в точечную нотацию - PullRequest
1 голос
/ 18 июня 2020

У меня есть массив, который на самом деле представляет собой древовидный массив:

array:2 [▼
  0 => array:7 [▼
    "id" => 36
    "attribute_key" => "amount"
    "attribute_value" => "Amount"
    "input_type_id" => 3
    "is_required" => 1
    "parent_id" => null
  ]
  1 => array:8 [▼
    "id" => 37
    "attribute_key" => "products"
    "attribute_value" => "Products"
    "input_type_id" => 7
    "is_required" => 1
    "parent_id" => null
    "event" => null
    "children" => array:2 [▼
      0 => array:7 [▼
        "id" => 38
        "attribute_key" => "product_name"
        "attribute_value" => "Product Name"
        "input_type_id" => 1
        "is_required" => 1
        "parent_id" => 37
      ]
      1 => array:7 [▼
        "id" => 39
        "attribute_key" => "price"
        "attribute_value" => "Price"
        "input_type_id" => 3
        "is_required" => 1
        "parent_id" => 37
      ]
    ]
  ]
]

, и я хотел бы получить что-то вроде этого:

[
     'amount' => 'required',
     'products.*.product_name' => 'required',
     'products.*.price' => 'required|numeric',
]

Мои данные очень динамичны c и я хотел бы создать правила проверки для Laravel.

Вот что у меня есть:

class EventRules
{

    protected $rules = [];

    public function rules(array $attributes) : array
    {
        foreach ($attributes as $attribute) {
            $this->addRules($attribute);
        }

        return $this->rules;
    }

    public function addRules($attribute) : void
    {
          if (isset($attribute['children'])) {
            $this->rules($attribute['children']);
            return;
        }

        $attributeKey = $attribute['attribute_key'];

        $rule = '';

        $rule .= $this->addRequiredRule($attribute);
        $rule .= $this->addFieldTypeRule($attribute);

        $this->rules[$attributeKey] = $rule;
    }

    protected function addRequiredRule($attribute) : string
    {
        $rule = '';

        if ($attribute['is_required'] === 1) {
            $rule .= 'required|';
        }

        // The rest will be here..


        return $rule;
    }

    protected function addFieldTypeRule($attribute) : string
    {
        $rule = [
            InputTypeEnum::STRING_TYPE => 'string',
            InputTypeEnum::INTEGER_TYPE => 'integer',
            InputTypeEnum::DECIMAL_TYPE => 'numeric',
            InputTypeEnum::DATE_TYPE => 'date',
        ];

        return $rule[$attribute['input_type_id']];
    }
}

В любом случае, я застрял с созданием ключа правила (с *) . Я знаю, что мне нужна рекурсия, которую я использую, но все еще не знаю, как справиться с остальными.

Спасибо.

1 Ответ

1 голос
/ 18 июня 2020

Я понял это и сделал отправную точку:

// Enum class for field types
final class InputTypeEnum {
    const STRING_TYPE = 1;
    const INTEGER_TYPE = 2;
    const DECIMAL_TYPE = 3;
    const DATE_TYPE = 4;
    const DATE_EMAIL = 7;
}

// Here is class to flat the rules.

class RulesFlattener {

    const ATTRIBUTE_KEY = 'attribute_key';
    const CHILD_KEY = 'children';

    private $input;
    private $output = [];

    // This array keeps map to translate rules
    private $availableRules = [
        'is_required' => 'required',
        'input_type_id' => [
            InputTypeEnum::STRING_TYPE => 'string',
            InputTypeEnum::INTEGER_TYPE => 'integer',
            InputTypeEnum::DECIMAL_TYPE => 'numeric',
            InputTypeEnum::DATE_TYPE => 'date',
            InputTypeEnum::DATE_EMAIL => 'email',
        ]
    ];

    public function __construct($input) {
        $this->input = $input;
    }

    private function extractRules($row) {
        $rules = [];

        foreach($row as $k => $v) {
            if(isset($this->availableRules[$k])) {

                $mappedRule = $this->availableRules[$k];

                if(is_array($mappedRule)) {
                    $rule = $mappedRule[$v] ?? null;
                } else {
                    $rule = $mappedRule;
                }

                $rules[] = $rule;
            }
        }

        return array_unique($rules);
    }

    public function parse() {
        return $this->parseRow($this->input);
    }

    private function parseRow($input, $parentKey = null) {
        $output = [];
        foreach ($input as $row) {

            // Keep name of current attribute (for recursion)
            $currentAttribute = $row[self::ATTRIBUTE_KEY] ?? null;

            // If you want get more nested rules like product.*.photos.*.url use this:
            // $currentAttribute = ( $parentKey ? $parentKey.'.*.':'') . $row[self::ATTRIBUTE_KEY] ?? null;

            foreach($row as $k => $v) {
                switch($k) {

                    case self::ATTRIBUTE_KEY:
                        $rules = $this->extractRules($row);
                        $output[($parentKey?$parentKey.'.*.':'').$v] = implode('|', $rules);
                        break;

                    case self::CHILD_KEY:
                        $output = array_merge($output, $this->parseRow($row[$k], $currentAttribute));
                        break;

                }
            }
        }
        return $output;
    }

}


Теперь вы можете использовать его как:

$dataIn = [
    [
        "id" => 36,
        "attribute_key" => "amount",
        "attribute_value" => "Amount",
        "input_type_id" => 3,
        "is_required" => 1,
        "parent_id" => null,
    ],
    [
        "id" => 37,
        "attribute_key" => "products",
        "attribute_value" => "Products",
        "input_type_id" => 7,
        "is_required" => 1,
        "parent_id" => null,
        "event" => null,
        "children" => [
            [
                "id" => 38,
                "attribute_key" => "product_name",
                "attribute_value" => "Product Name",
                "input_type_id" => 1,
                "is_required" => 1,
                "parent_id" => 37,
            ],
            [
                "id" => 39,
                "attribute_key" => "price",
                "attribute_value" => "Price",
                "input_type_id" => 3,
                "is_required" => 1,
                "parent_id" => 37,
            ]
        ]
    ]
];

$flat = new RulesFlattener($dataIn);

$rules = $flat->parse();

, и я получаю такой результат:

Array
(
    [amount] => numeric|required
    [products] => email|required
    [products.*.product_name] => string|required
    [products.*.price] => numeric|required
)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...