Search code examples
phpyii2

insert an associative array into the Yii2 migration


There are 4 tables in the database: months, types, prices, tonnages
There is an array of the following type:

'prices'=> [
        'soy' => [
          'january' => [
                50 => 145,
                75 => 136,
                100 => 138
            ],
           'february' => [
                50 => 100,
                75 => 125,
                100 => 108
            ],
            ....
          ],
        'hay' => 
            'january' => [
                50 => 105,
                75 => 116,
                100 => 118
            ],
           'february' => [
                50 => 100,
                75 => 125,
                100 => 108
            ],
        ....

This array is an example of data that should be loaded into a database by migrating to a table named prices.
The prices table has the same fields as: month_id, tonnage_id, type_id, price.
The months, types, and tonnages tables are filled in. How can I compare the data and upload it to the database?

I have a code that already works, but it seems to me that it turned out to be unnecessarily complicated.

public function safeUp()
{
   $months = $this->getDb()->createCommand('SELECT * FROM months')->queryAll();
   $tonnages = $this->getDb()->createCommand('SELECT * FROM tonnages')->queryAll();
   $types = $this->getDb()->createCommand('SELECT * FROM raw_types')->queryAll();
   $columns = ['price', 'month_id', 'tonnage_id', 'type_id'];
   $rows =  MigrationHelper::toRecords(Yii::$app->params['prices'], $months, $tonnages, $types);
   $this->batchInsert('prices', $columns, $rows);
}
public static function toRecords(array $arr, array $months, array $tonnages, array $types): array
{
  $result = [];
  foreach ($arr as $type => $typeValues) {
     $typeId = self::findIdByField($types, 'name', $type);
     foreach ($typesValues as $month => $monthValue) {
        $monthId = self::findIdByField($types, 'name', $month);
         foreach ($monthValues as $tonnage => $price) {
           $tonnageId = self::findIdByField($types, 'value', $tonnag);
           $result[] = [
              'price' = $price,
              'month_id' => $monthId,
              'tonnage_id' => $tonnageId,
              'type_id' => $typeId
          ];
         }
      }
  }
  return $result;
}

private static function findIdByField(array $arr, string $fieldName, int | string $value) : int 
{
  foreach ($arr as $item) {
   if ($item[$fieldName] === $value) {
      return $item['id'];
   }
  }
}

Solution

  • I'm going to assume queryAll() simply returns an array of associative arrays. You should convert them to associative arrays keyed off the fields you want to search, rather than using findIdByField(), which is presumably a linear search.

    public static function toRecords(array $arr, array $months, array $tonnages, array $types): array
    {
        $result = [];
        $types = array_combine(array_column($types, 'name'), array_column($types, 'id'));
        $months = array_combine(array_column($months, 'name'), array_column($months, 'id'));
        $tonnages = array_combine(array_column($tonnage, 'value'), array_column($tonnage, 'id'));
    
        foreach ($arr as $type => $typeValues) {
            $typeId = $types[$type];
            foreach ($typesValues as $month => $monthValue) {
                $monthId = $months[$month]
                foreach ($monthValues as $tonnage => $price) {
                    $tonnageId = $tonnages[$tonnage];
                    $result[] = [
                        'price' = $price,
                        'month_id' => $monthId,
                        'tonnage_id' => $tonnageId,
                        'type_id' => $typeId
                    ];
                }
            }
        }
        return $result;
    }