Отменить точечный массив с подстановочными знаками в Laravel

В Laravel я создаю карту JSON, где мне нужно преобразовать JSON в другую схему. Для этого я выполняю следующие шаги:

  1. Расставьте точки над массивом с помощью помощника Arr::dot. Таким образом, у меня есть такой результат, пример:
array(11) {
  ["0.identificador"]=>
  int(1)
  ["0.nome_completo"]=>
  string(8) "John Doe"
  ["0.email"]=>
  string(11) "[email protected]"
  ["0.empresa.razao_social"]=>
  string(11) "ABC Company"
  ["0.empresa.endereco"]=>
  string(11) "123 Main St"
  ["0.pedidos.0.SKU"]=>
  int(1)
  ["0.pedidos.0.descricao"]=>
  string(5) "Shoes"
  ["0.pedidos.0.qtd"]=>
  int(2)
  ["0.pedidos.1.SKU"]=>
  int(2)
  ["0.pedidos.1.descricao"]=>
  string(5) "Shirt"
  ["0.pedidos.1.qtd"]=>
  int(1)
}
  1. Переберите пунктирный массив и сравните его с моей картой ключей. Если они совпадают, я устанавливаю значение с помощью сопоставленного ключа.

Моя карта выглядит так:

array(8) {
    ["*.identificador"]=> string(4) "*.id"
    ["*.nome_completo"]=> string(6) "*.name"
    ["*.empresa"]=> string(9) "*.company"
    ["*.empresa.razao_social"]=> string(14) "*.company.name"
    ["*.empresa.endereco"]=> string(17) "*.company.address"
    ["*.pedidos.*.SKU"]=> string(13) "*.orders.*.id"
    ["*.pedidos.*.descricao"]=> string(18) "*.orders.*.product"
    ["*.pedidos.*.qtd"]=> string(19) "*.orders.*.quantity"
  }

И вот как выглядит мой код:


$payload = [
    [
        "identificador" => 1,
        "nome_completo" => "John Doe",
        "email" => "[email protected]",
        "empresa" => [
            "razao_social" => "ABC Company",
            "endereco" => "123 Main St"
        ],
        "pedidos" => [
            [
                "SKU" => 1,
                "descricao" => "Shoes",
                "qtd" => 2
            ],
            [
                "SKU" => 2,
                "descricao" => "Shirt",
                "qtd" => 1
            ]
        ]
    ]
];

$dottedPayload = Arr::dot($payload);

$transformedPayload = [];
foreach ($dottedPayload as $key => $value) {
    $newKey = preg_replace('/\d+./', '*.', $key);
    $newKey = preg_replace('/\.\d+\./', '.*.', $newKey);
    $newKey = preg_replace('/\.\d+$/', '.*', $newKey);

    $mappedKey = $mappingConfig[$newKey] ?? $key;

    data_fill($transformedPayload, $mappedKey, $value);
}

У меня есть такой результат:

array(2) {
  ["*"]=>
  array(4) {
    ["id"]=>
    int(1)
    ["name"]=>
    string(8) "John Doe"
    ["company"]=>
    array(2) {
      ["name"]=>
      string(11) "ABC Company"
      ["address"]=>
      string(11) "123 Main St"
    }
    ["orders"]=>
    array(1) {
      ["*"]=>
      array(3) {
        ["id"]=>
        int(2)
        ["product"]=>
        string(5) "Shirt"
        ["quantity"]=>
        int(1)
      }
    }
  }
  [0]=>
  array(1) {
    ["email"]=>
    string(11) "[email protected]"
  }
}

Но это ожидаемо:

$expectedResult = [
    [
        "id" => 1,
        "name" => "John Doe",
        "email" => "[email protected]",
        "company" => [
            "name" => "ABC Company",
            "address" => "123 Main St"
        ],
        "orders" => [
            [
                "id" => 1,
                "product" => "Shoes",
                "quantity" => 2
            ],
            [
                "id" => 2,
                "product" => "Shirt",
                "quantity" => 1
            ]
        ]
    ]
];

Как я могу правильно отменить массив после сопоставления с помощью подстановочных знаков и правильно установить индексы? Также приветствуются любые другие предложения по выполнению этого сопоставления JSON.

«Отменить Dot Array в Laravel» Вы пробовали метод Arr::undot?

Tpojka 31.03.2024 00:22

Да! Я уже испробовал все помощники, но не смог добиться ожидаемого результата. Интересно, возможно ли это без написания собственной рекурсивной функции для обработки подстановочных знаков?

Gabriel Baierle 31.03.2024 00:59
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
1
2
146
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Мне нравится реализовывать сопоставление данных с помощью пакета Spatie Laravel Data.

Пакет Spatie использует классы для определения объектов данных, которые заполняются из массивов или данных JSON. Имена свойств — это имена полей, которые вы хотите использовать в своем коде, а пакет предоставляет модификатор атрибута MapInputName для автоматического перевода имен входящих данных.

Сначала мы определяем классы данных и сопоставление полей:

use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\Attributes\MapInputName;
use Spatie\LaravelData\DataCollection;

class Company extends Data
{
  public function __construct(
    #[MapInputName('razao_social')] 
    public string $name,
    #[MapInputName('endereco')] 
    public string $address
  ) { }
}

class Order extends Data
{
  public function __construct(
    #[MapInputName('SKU')] 
    public int $id,
    #[MapInputName('descricao')] 
    public string $product,
    #[MapInputName('qtd')] 
    public int $quantity
  ) { }
}

class Payload extends Data
{
  public function __construct(
    #[MapInputName('identificador')] 
    public int $id,
    #[MapInputName('nome_completo')] 
    public string $name,
    #[MapInputName('empresa')]
    public Company $company,
    #[MapInputName('pedidos')]
    #[DataCollectionOf(Order::class)]
    public DataCollection $orders
  ) { }
}

Когда вы получаете полезные данные, передайте их статическому конструктору from(...) или используйте внедрение зависимостей, чтобы получить данные из запроса:

$payload = [
    [
        "identificador" => 1,
        "nome_completo" => "John Doe",
        "email" => "[email protected]",
        "empresa" => [
            "razao_social" => "ABC Company",
            "endereco" => "123 Main St"
        ],
        "pedidos" => [
            [
                "SKU" => 1,
                "descricao" => "Shoes",
                "qtd" => 2
            ],
            [
                "SKU" => 2,
                "descricao" => "Shirt",
                "qtd" => 1
            ]
        ]
    ]
];

Payload::from($payload[0])->toArray();

Функция toArray дает результат:

[
  "id" => 1,
  "name" => "John Doe",
  "company" => [
    "name" => "ABC Company",
    "address" => "123 Main St",
  ],
  "orders" => [
    [
      "id" => 1,
      "product" => "Shoes",
      "quantity" => 2,
    ],
    [
      "id" => 2,
      "product" => "Shirt",
      "quantity" => 1,
    ],
  ],
]

Пакет не только предоставляет эти сопоставления, но также может проверять данные с помощью модификаторов атрибутов и передавать их в Model::create, чтобы легко создавать объекты Eloquent.

Это очень круто. Вам когда-нибудь приходилось обрабатывать данные, которые может настроить пользователь? Например: клиент API, в котором пользователь может настроить API для получения данных и отправки данных, преобразованных в другой API, все поля, которые будут сопоставлены, настраиваются пользователем. Можно ли как-то настроить сопоставления в этом сценарии?

Gabriel Baierle 01.04.2024 02:46

Вы не упомянули, что отображение должно настраиваться пользователем;) Пакет Spatie не совсем подходит для этой ситуации, поскольку вам нужно определить классы данных. У меня есть альтернативное решение, которое я опубликую как отдельный ответ.

Tony 01.04.2024 23:30
Ответ принят как подходящий

Поскольку вы хотите, чтобы сопоставление имен клавиш настраивалось пользователем, вы можете изменить свой первоначальный подход, чтобы использовать группы захвата, при этом числовые счетчики будут сохраняться с помощью функции Arr::dot.

$payload = [
    [
        "identificador" => 1,
        "nome_completo" => "John Doe",
        "email" => "[email protected]",
        "empresa" => [
            "razao_social" => "ABC Company",
            "endereco" => "123 Main St"
        ],
        "pedidos" => [
            [
                "SKU" => 1,
                "descricao" => "Shoes",
                "qtd" => 2
            ],
            [
                "SKU" => 2,
                "descricao" => "Shirt",
                "qtd" => 1
            ]
        ]
    ]
];

$regexMapping = [
  'identificador' => 'id',
  'nome_completo' => 'name',
  'email' => 'email',
  'empresa\.razao_social' => 'company.name',
  'empresa\.endereco' => 'company.address',
  'pedidos\.(\d+)\.SKU' => 'orders.$1.id',
  'pedidos\.(\d+)\.descricao' => 'orders.$1.product',
  'pedidos\.(\d+)\.qtd' => 'orders.$1.quantity',
];

$dotted = Arr::dot($payload);
$newPayload = [];
$replacements = 0;

foreach ($dotted as $key => $value) {
  foreach ($regexMapping as $pattern => $replacement) {
    $newKey = preg_replace('/'.$pattern.'/', $replacement, $key, -1, $replacements);
    if ($replacements) {
      // only store the new key if we replaced something
      $newPayload[$newKey] = $value;
      // match found, no need to check any more patterns
      break; 
    }
  }
}

Arr::undot($newPayload);

Что дает результат:

[
  [
    "id" => 1,
    "name" => "John Doe",
    "email" => "[email protected]",
    "company" => [
      "name" => "ABC Company",
      "address" => "123 Main St",
    ],
    "orders" => [
      [
        "id" => 1,
        "product" => "Shoes",
        "quantity" => 2,
      ],
      [
        "id" => 2,
        "product" => "Shirt",
        "quantity" => 1,
      ],
    ],
  ],
]

Неэффективностью этого подхода является вложенный цикл; но чтобы понять, как это работает, и если у вас всего несколько сопоставлений, это не является большой проблемой.

Спасибо за ваше предложение. Исходя из этого, для решения моей проблемы использовать это сопоставление регулярных выражений было намного проще, чем использовать рекурсию для обработки подстановочных знаков.

Gabriel Baierle 02.04.2024 18:53

Другие вопросы по теме