'array']); } /** * Laravel вызывает этот хук из трейта RefreshDatabase сразу после migrate:fresh. * * migrate:fresh пересоздаёт только родительские партиционированные таблицы; * месячные партиции — отдельная команда (только вперёд). Тесты с датами * прошлых месяцев иначе падают на отсутствии партиции. Диапазон: 2 назад + * текущий + 3 вперёд. Идемпотентно. * * DDL идёт через pgsql_supplier (как в MonthlyPartitionManager::DDL_CONNECTION), * иначе на проде crm_app_user не имеет прав создавать партиции. */ protected function afterRefreshingDatabase() { Artisan::call('partitions:create-months', ['--ahead' => 3]); // Окно партиций считаем от РЕАЛЬНОГО системного времени (new DateTimeImmutable), // а не от now()/Carbon: отдельные тесты замораживают Carbon::setTestNow на // прошлые/будущие даты, и тогда единственный migrate:fresh пересоздавал бы // месяцы вокруг замороженной даты, не покрывая реальный прошлый месяц (тесты // с датами вроде now()->subDays(10) падали на отсутствии партиции). Диапазон // -3..+5 месяцев. Идемпотентно. DDL через pgsql_supplier (DDL_CONNECTION) — // у crm_app_user нет прав на создание партиций. $base = new \DateTimeImmutable('first day of this month 00:00:00'); $parents = DB::select("select c.relname from pg_class c join pg_partitioned_table p on p.partrelid = c.oid where c.relkind = 'p'"); for ($m = -3; $m <= 5; $m++) { $start = $base->modify(($m >= 0 ? '+' : '-').abs($m).' months'); $end = $start->modify('+1 month'); $suffix = '_y'.$start->format('Y').'_m'.$start->format('m'); foreach ($parents as $p) { $t = $p->relname; DB::connection('pgsql_supplier')->statement( "CREATE TABLE IF NOT EXISTS \"{$t}{$suffix}\" PARTITION OF \"{$t}\" FOR VALUES FROM ('{$start->format('Y-m-d')}') TO ('{$end->format('Y-m-d')}')" ); } } } }