diff --git a/src/Factories/Concerns/CreatesEntry.php b/src/Factories/Concerns/CreatesEntry.php index 92e67c7..a291878 100644 --- a/src/Factories/Concerns/CreatesEntry.php +++ b/src/Factories/Concerns/CreatesEntry.php @@ -24,9 +24,13 @@ public function newModel(array $attributes = []) $entry->slug($slug); } - if (($site = Arr::pull($attributes, 'site')) && $entry->sites()->contains($site)) { - $entry->locale($site); - } + $site = Arr::pull($attributes, 'site'); + + $site = $entry->sites()->contains($site) + ? $site + : $entry->sites()->first(); + + $entry->locale($site); $entry->published(Arr::pull($attributes, 'published', true)); diff --git a/src/Factories/Concerns/WithSites.php b/src/Factories/Concerns/WithSites.php index 054acbf..637e00f 100644 --- a/src/Factories/Concerns/WithSites.php +++ b/src/Factories/Concerns/WithSites.php @@ -2,6 +2,7 @@ namespace Aerni\Factory\Factories\Concerns; +use Statamic\Facades\Site; use Illuminate\Support\Collection; use Statamic\Contracts\Entries\Entry; use Statamic\Contracts\Taxonomies\Term; @@ -15,23 +16,27 @@ public function inSite(string $site): self public function inRandomSite(): self { - return $this->sequence(fn () => ['site' => $this->getSitesFromContentModel()->random()]); + return $this->state(fn () => ['site' => $this->getSitesFromContentModel()->random()]); } public function perSite(): self { $sites = $this->getSitesFromContentModel()->map(fn ($site) => ['site' => $site]); + // TODO: This won't work if count() is used after perSite(). Can we make this work with a callback? return $this->sequence(...$sites)->count(($this->count ?? 1) * $sites->count()); } protected function getSitesFromContentModel(): Collection { - $contentModel = $this->newModel(); - - return match (true) { - $contentModel instanceof Entry => $contentModel->sites(), - $contentModel instanceof Term => $contentModel->taxonomy()->sites(), - }; + return once(function () { + $contentModel = $this->newModel(); + + return match (true) { + $contentModel instanceof Entry => $contentModel->sites(), + $contentModel instanceof Term => $contentModel->taxonomy()->sites(), + default => Site::all()->map->handle(), + }; + }); } } diff --git a/src/Factories/Factory.php b/src/Factories/Factory.php index 13335bb..f9f319d 100644 --- a/src/Factories/Factory.php +++ b/src/Factories/Factory.php @@ -4,14 +4,16 @@ use Closure; use Faker\Generator; -use Illuminate\Container\Container; -use Illuminate\Database\Eloquent\Factories\CrossJoinSequence; -use Illuminate\Database\Eloquent\Factories\Sequence; +use Statamic\Facades\Site; +use Illuminate\Support\Arr; use Illuminate\Support\Collection; -use Illuminate\Support\Traits\Conditionable; -use Illuminate\Support\Traits\Macroable; +use Illuminate\Container\Container; use Statamic\Contracts\Entries\Entry; use Statamic\Contracts\Taxonomies\Term; +use Illuminate\Support\Traits\Macroable; +use Illuminate\Support\Traits\Conditionable; +use Illuminate\Database\Eloquent\Factories\Sequence; +use Illuminate\Database\Eloquent\Factories\CrossJoinSequence; abstract class Factory { @@ -160,6 +162,7 @@ protected function getExpandedAttributes(): array protected function getRawAttributes(): array { return $this->states + ->pipe($this->evaluateSiteStates(...)) ->reduce(function (array $carry, $state) { if ($state instanceof Closure) { $state = $state->bindTo($this); @@ -169,6 +172,30 @@ protected function getRawAttributes(): array }, $this->definition()); } + protected function evaluateSiteStates(Collection $states): Collection + { + $evaluatedSiteStates = $states + ->map(fn ($state) => (clone $state)()) /* Clone the closure so that we don't run into issues when evaluating the same closure later. Needed for sequences to work correctly. */ + ->filter(fn ($state) => isset($state['site'])); + + if ($evaluatedSiteStates->isEmpty()) { + return $states; + } + + $siteState = $evaluatedSiteStates->last(); + + $site = $this->getSitesFromContentModel()->flip()->has($siteState['site']) + ? Site::get($siteState['site']) + : Site::get($this->getSitesFromContentModel()->first()); + + $this->faker = Container::getInstance()->makeWith(Generator::class, ['locale' => $site->locale()]); + + // TODO: This breaks the perSite() sequence but works for inSite() and inRandomSite(). + return $states->diffKeys($evaluatedSiteStates) + ->push(fn () => ['site' => $site->handle()]) + ->values(); + } + protected function expandAttributes(array $definition) { return collect($definition) @@ -289,7 +316,10 @@ protected function newInstance(array $arguments = []): self protected function withFaker(): Generator { - return Container::getInstance()->make(Generator::class); + // TODO: This won't work for new content models that don't use the WithSites trait. + $site = Site::get($this->getSitesFromContentModel()->first()); + + return Container::getInstance()->makeWith(Generator::class, ['locale' => $site->locale()]); } public function modelName(): string diff --git a/tests/FactoryTest.php b/tests/FactoryTest.php index d42bb5c..a4fb3c3 100644 --- a/tests/FactoryTest.php +++ b/tests/FactoryTest.php @@ -344,6 +344,7 @@ public function test_entry_can_be_created_in_site() $entries = FactoryTestEntryFactory::times(5)->perSite()->create(); $this->assertCount(10, $entries); + dd($entries->map->locale()); $entries->each(fn ($entry, $index) => $this->assertSame($index % 2 === 0 ? 'default' : 'german', $entry->locale())); }