247 lines
8.5 KiB
PHP
247 lines
8.5 KiB
PHP
<?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.');
|
|
}
|
|
}
|