Initial commit

This commit is contained in:
Ronald Huynen
2026-03-23 21:37:59 +01:00
commit 2547717edb
2193 changed files with 972171 additions and 0 deletions

View File

@@ -0,0 +1,246 @@
<?php
namespace Database\Seeders;
use App\Models\Call;
use App\Models\CallTranslation;
use App\Models\Locations\Location;
use App\Models\User;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
/**
* Temporary seeder for search testing only. NOT for production.
* Assigns random tags and calls to users that don't have them.
*
* Run: php artisan db:seed --class=TestSearchDataSeeder
* Undo: php artisan db:seed --class=TestSearchDataSeeder --rollback
* (or pass --rollback flag)
*/
class TestSearchDataSeeder extends Seeder
{
// How many users to seed (pick randomly from those without tags/calls)
private const USERS_TO_SEED = 200;
private const TAGS_PER_USER = 3;
private const CALLS_PER_USER = 2;
public function run(bool $rollback = false): void
{
if ($rollback || in_array('--rollback', $_SERVER['argv'] ?? [])) {
$this->rollback();
return;
}
$this->seedTags();
$this->seedCalls();
$this->command->info('Re-indexing affected users in Elasticsearch (synchronously)...');
$this->reindexAffectedUsers();
$this->command->info('Done. Run with --rollback to remove test data.');
}
private function seedTags(): void
{
$users = User::doesntHave('tags')
->inRandomOrder()
->limit(self::USERS_TO_SEED)
->get();
// Only use tag_ids that have a context with a non-null category_id
$tagIds = DB::table('taggable_contexts')
->whereNotNull('category_id')
->inRandomOrder()->limit(500)->pluck('id')->toArray();
if (empty($tagIds)) {
$this->command->warn('No tags found, skipping tag seeding.');
return;
}
$rows = [];
$now = now()->toDateTimeString();
foreach ($users as $user) {
$selected = collect($tagIds)->shuffle()->take(self::TAGS_PER_USER);
foreach ($selected as $tagId) {
$rows[] = [
'tag_id' => $tagId,
'taggable_id' => $user->id,
'taggable_type' => 'App\Models\User',
'created_at' => $now,
'updated_at' => $now,
];
}
}
// Insert in chunks, ignore duplicates
foreach (array_chunk($rows, 500) as $chunk) {
DB::table('taggable_taggables')->insertOrIgnore($chunk);
}
$this->command->info('Seeded tags for ' . $users->count() . ' users.');
}
private function seedCalls(): void
{
$users = User::with(['locations.country', 'locations.division', 'locations.city', 'locations.district'])
->inRandomOrder()
->limit(self::USERS_TO_SEED)
->get();
// Only use tag_ids that exist in taggable_tags AND have a context with a non-null category_id
$validTagIds = DB::table('taggable_tags')
->whereIn('tag_id', DB::table('taggable_contexts')->whereNotNull('category_id')->pluck('id'))
->inRandomOrder()->limit(100)->pluck('tag_id')->toArray();
if (empty($validTagIds)) {
$this->command->warn('No valid tags found, skipping call seeding.');
return;
}
// Fetch tag names for generating content
$tagNames = DB::table('taggable_tags')
->whereIn('tag_id', $validTagIds)
->pluck('name', 'tag_id');
$count = 0;
$now = now();
foreach ($users as $user) {
$selected = collect($validTagIds)->shuffle()->take(self::CALLS_PER_USER);
// Resolve location from user's primary location
$locationId = $this->resolveUserLocation($user);
foreach ($selected as $tagId) {
// Random till date between 1 and 3 months from now
$tillMonths = rand(1, 3);
$till = $now->copy()->addMonths($tillMonths);
$call = Call::create([
'callable_id' => $user->id,
'callable_type' => 'App\Models\User',
'tag_id' => $tagId,
'location_id' => $locationId,
'from' => $now->utc(),
'till' => $till,
'is_public' => true,
'is_suppressed' => false,
'is_paused' => false,
]);
$tagName = $tagNames[$tagId] ?? 'this skill';
CallTranslation::create([
'call_id' => $call->id,
'locale' => 'en',
'content' => "Looking to connect with others around {$tagName}. Available for the coming months.",
]);
$count++;
}
}
$this->command->info("Seeded {$count} calls for " . $users->count() . ' users.');
}
/**
* Resolve or create a standalone Location record from the user's primary location.
*/
private function resolveUserLocation(User $user): ?int
{
$userLocation = $user->locations->first();
if (!$userLocation) {
return null;
}
$attributes = array_filter([
'country_id' => $userLocation->country_id ?: null,
'division_id' => $userLocation->division_id ?: null,
'city_id' => $userLocation->city_id ?: null,
'district_id' => $userLocation->district_id ?: null,
]);
if (empty($attributes)) {
return null;
}
// Find existing standalone Location (no locatable_id/type) or create one
$location = Location::whereNull('locatable_id')
->whereNull('locatable_type')
->where($attributes)
->first();
if (!$location) {
$location = new Location($attributes);
$location->save();
}
return $location->id;
}
private function reindexAffectedUsers(): void
{
// Disable queue so indexing happens synchronously with full relationship loading
config(['scout.queue' => false]);
$cutoff = now()->subMinutes(10)->toDateTimeString();
$userIds = DB::table('taggable_taggables')
->where('taggable_type', 'App\Models\User')
->where('created_at', '>=', $cutoff)
->pluck('taggable_id')
->merge(
DB::table('calls')->where('created_at', '>=', $cutoff)->pluck('callable_id')
)
->unique();
$bar = $this->command->getOutput()->createProgressBar($userIds->count());
User::whereIn('id', $userIds)->each(function (User $user) use ($bar) {
$user->searchable();
$bar->advance();
});
$bar->finish();
$this->command->newLine();
// Also re-index the calls themselves
$this->command->info('Re-indexing calls in Elasticsearch...');
config(['scout.queue' => false]);
$callIds = DB::table('calls')->where('created_at', '>=', $cutoff)->pluck('id');
$callBar = $this->command->getOutput()->createProgressBar($callIds->count());
Call::whereIn('id', $callIds)->each(function (Call $call) use ($callBar) {
$call->searchable();
$callBar->advance();
});
$callBar->finish();
$this->command->newLine();
}
private function rollback(): void
{
// Remove only the test-seeded data (created recently)
$cutoff = now()->subHour()->toDateTimeString();
// Delete call_translations first (FK constraint), then calls
$callIds = DB::table('calls')
->where('created_at', '>=', $cutoff)
->pluck('id');
$translationsDeleted = DB::table('call_translations')
->whereIn('call_id', $callIds)
->delete();
$this->command->info("Removed {$translationsDeleted} test call translations.");
$deleted = DB::table('calls')
->where('created_at', '>=', $cutoff)
->delete();
$this->command->info("Removed {$deleted} test calls.");
$deleted = DB::table('taggable_taggables')
->where('taggable_type', 'App\Models\User')
->where('created_at', '>=', $cutoff)
->delete();
$this->command->info("Removed {$deleted} test tag assignments.");
$this->command->info('Re-indexing affected users in Elasticsearch (synchronously)...');
$this->reindexAffectedUsers();
$this->command->info('Rollback complete.');
}
}