<?php
/**
* Class to handle concerts
*
* @package giglogadmin
*
* SPDX-FileCopyrightText: 2021 Andrea Chirulescu <andrea.chirulescu@gmail.com>
* SPDX-FileCopyrightText: 2021 Harald Eilertsen <haraldei@anduin.net>
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
if ( ! class_exists( 'GiglogAdmin_Concert' ) ) {
/**
* Class to hold all information about a given concert.
*/
class GiglogAdmin_Concert {
// phpcs:disable Squiz.Commenting.VariableComment.Missing
private ?int $id;
private ?string $cname;
private ?GiglogAdmin_Venue $venue;
private DateTimeImmutable $cdate;
private ?string $tickets;
private ?string $eventlink;
private ?int $status;
private array $roles;
private ?DateTimeImmutable $created;
private ?DateTimeImmutable $updated;
// phpcs:enable
public const STATUS_NONE = 0;
public const STATUS_ACCRED_REQ = 1;
public const STATUS_PHOTO_APPROVED = 2;
public const STATUS_TEXT_APPROVED = 3;
public const STATUS_ALL_APPROVED = 4;
public const STATUS_REJECTED = 5;
/**
* Constructs a new concert object from an array of attributes.
*
* The attributes are expected to be named as in the database,
* so this constructor can be used to construct the object
* directly from the database row.
*
* @param object $attrs an object or array with the attributes.
*/
public function __construct( object $attrs ) {
$this->id = isset( $attrs->id ) ? $attrs->id : null;
$this->cname = isset( $attrs->wpgconcert_name ) ? $attrs->wpgconcert_name : null;
$this->cdate = new DateTimeImmutable( $attrs->wpgconcert_date ?? 'now' );
$this->tickets = isset( $attrs->wpgconcert_tickets ) ? $attrs->wpgconcert_tickets : null;
$this->eventlink = isset( $attrs->wpgconcert_event ) ? $attrs->wpgconcert_event : null;
$this->status = isset( $attrs->wpgconcert_status ) ? $attrs->wpgconcert_status : 0;
$this->roles = isset( $attrs->wpgconcert_roles ) ? json_decode( $attrs->wpgconcert_roles, true ) : array();
$this->created = isset( $attrs->created ) ? new DateTimeImmutable( $attrs->created ) : null;
$this->updated = isset( $attrs->updated ) ? new DateTimeImmutable( $attrs->updated ) : null;
if ( isset( $attrs->venue ) ) {
if ( isset( $attrs->wpgvenue_name ) && isset( $attrs->wpgvenue_city ) ) {
$venue_attrs = (object) array(
'id' => $attrs->venue,
'wpgvenue_name' => $attrs->wpgvenue_name,
'wpgvenue_city' => $attrs->wpgvenue_city,
);
$this->venue = new GiglogAdmin_Venue( $venue_attrs );
} else {
$this->venue = GiglogAdmin_Venue::get( $attrs->venue );
}
} else {
$this->venue = null;
}
}
/**
* Return the concert with the given id.
*
* @param int $id Database id of the concert to fetch.
* @return null|self
*/
public static function get( int $id ) : ?self {
$gigs = self::find_concerts( array( 'id' => $id ) );
$count = count( $gigs );
if ( $count > 1 ) {
error_log(
"Found more than one concert with the same id ({$id})."
. ' This should not happen!'
);
wp_die( 'Oops! Something went wrong', 500 );
} else if ( $count != 1 ) {
return null;
} else {
return array_shift( $gigs );
}
}
/**
* Create a new concert object.
*
* @param string $name The concert description.
* @param int $venue_id The id of the venue where the concert will be held.
* @param string $date The date of the concert.
* @param string $ticketlink URL where tickets can be bough.
* @param string $eventlink URL for more information about concert.
*
* @throws GiglogAdmin_DuplicateConcertException If concert is a duplicate.
*/
public static function create( string $name, int $venue_id, string $date, string $ticketlink, string $eventlink ): ?self {
$gigs = self::find_concerts(
array(
'name' => $name,
'venue_id' => $venue_id,
'date' => $date,
)
);
if ( ! empty( $gigs ) ) {
throw new GiglogAdmin_DuplicateConcertException(
"Duplicate concert: name: {$name}, venue_id: {$venue_id}, date: {$date}"
);
} else {
$concert = new GiglogAdmin_Concert(
(object) array(
'wpgconcert_name' => $name,
'venue' => $venue_id,
'wpgconcert_date' => $date,
'wpgconcert_tickets' => $ticketlink,
'wpgconcert_event' => $eventlink,
)
);
$concert->save();
return self::get( $concert->id() );
}
}
/**
* Update a concert with new content and save to database.
*
* @param object $attrs Object or array of changed attributes.
*/
public function update( object $attrs ) : bool {
$need_update = false;
if ( isset( $attrs->wpgconcert_name ) && $attrs->wpgconcert_name != $this->cname ) {
$this->cname = $attrs->wpgconcert_name;
$need_update = true;
}
if ( isset( $attrs->wpgconcert_date ) && $attrs->wpgconcert_date != $this->cdate ) {
$this->cdate = new DateTimeImmutable( $attrs->wpgconcert_date );
$need_update = true;
}
if ( isset( $attrs->wpgconcert_tickets ) && $attrs->wpgconcert_tickets != $this->tickets ) {
$this->tickets = $attrs->wpgconcert_tickets;
$need_update = true;
}
if ( isset( $attrs->wpgconcert_event ) && $attrs->wpgconcert_event != $this->eventlink ) {
$this->eventlink = $attrs->wpgconcert_eventlink;
$need_update = true;
}
if ( isset( $attrs->wpgconcert_status ) && $attrs->wpgconcert_status != $this->status ) {
$this->status = $attrs->wpgconcert_status;
$need_update = true;
}
if ( isset( $attrs->wpgconcert_roles ) && $attrs->wpgconcert_roles != $this->roles ) {
$this->roles = $attrs->wpgconcert_roles;
$need_update = true;
}
if ( isset( $attrs->venue ) && $attrs->venue != $this->venue()->id() ) {
$this->venue = GiglogAdmin_Venue::get( $attrs->venue );
$need_update = true;
}
if ( $need_update ) {
$this->save();
}
return $need_update;
}
/**
* Build a query with given filters.
*
* @param array $filter Filters to include in the query.
* @param bool $count Only return count of result if true.
*/
private static function _build_query( array $filter = array(), bool $count = false ) : string {
global $wpdb;
$ct = "{$wpdb->prefix}giglogadmin_concerts";
$vt = "{$wpdb->prefix}giglogadmin_venues";
if ( $count ) {
$query = "SELECT count({$ct}.id) ";
} else {
$query = "SELECT {$ct}.*, {$vt}.wpgvenue_name, {$vt}.wpgvenue_city ";
}
$query .= "FROM {$ct} LEFT JOIN {$vt} ON {$ct}.venue = {$vt}.id WHERE wpgconcert_date >= CURRENT_TIMESTAMP";
$keymap = array(
'id' => $wpdb->prefix . 'giglogadmin_concerts.id',
'name' => 'wpgconcert_name',
'date' => 'wpgconcert_date',
'month' => 'MONTH(wpgconcert_date)',
'venue_id' => $wpdb->prefix . 'giglogadmin_venues.id',
'venue' => $wpdb->prefix . 'giglogadmin_venues.wpgvenue_name',
'city' => $wpdb->prefix . 'giglogadmin_venues.wpgvenue_city',
'currentuser' => 'wpgconcert_roles',
);
$where = array();
$offset = 0;
$limit = 15;
$page = null;
foreach ( $filter as $key => $value ) {
switch ( $key ) {
case 'name':
case 'date':
case 'month':
case 'venue':
case 'city':
array_push( $where, $keymap[ $key ] . $wpdb->prepare( '=%s', $value ) );
break;
case 'id':
case 'venue_id':
array_push( $where, $keymap[ $key ] . $wpdb->prepare( '=%d', $value ) );
break;
case 'currentuser':
array_push( $where, $keymap[ $key ] . $wpdb->prepare( ' like %s', esc_like( $value ) ) );
break;
case 'page':
$page = intval( $value );
break;
case 'offset':
$offset = intval( $value );
break;
case 'limit':
$limit = intval( $value );
break;
}
}
if ( ! empty( $where ) ) {
$query .= ' AND ' . implode( ' and ', $where );
}
if ( $page !== null ) {
$offset = ( $page - 1 ) * $limit;
}
$query .= ' ORDER BY wpgconcert_date';
$query .= " LIMIT {$offset},{$limit}";
return $query;
}
/**
* Return an array of concert objects optionally limited by a specified
* filter.
*
* Valid filters are:
* - 'venue_id' => int : only include concerts at the given venue
* - 'city' => string : only include concerts in the given city
*
* @param array<string, mixed> $filter Filter to use for the query.
* @return array<GiglogAdmin_Concert>
*/
public static function find_concerts( array $filter = array() ) : array {
global $wpdb;
$query = self::_build_query( $filter, false );
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$results = $wpdb->get_results( $query );
return array_map( fn( $c ) => new GiglogAdmin_Concert( $c ), $results );
}
/**
* Return the number of objects matching the given filter.
*
* @param array<string, mixed> $filter The filter to use for the query.
* @return int
*/
public static function count( array $filter = array() ) : int {
global $wpdb;
$query = self::_build_query( $filter, true );
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$count = $wpdb->get_var( $query );
return $count ? $count : 0;
}
/**
* Return number of pages with the limit of concerts/page
* specified in the filter.
*
* @param array<string, mixed> $filter The filter to use for the query.
* @return number of pages.
*/
public static function count_pages( array $filter = array() ) : int {
if ( ! isset( $filter['limit'] ) ) {
$filter['limit'] = 15;
}
$num_concerts = self::count( $filter );
return ( intval( $num_concerts / $filter['limit'] ) ) + 1;
}
/**
* Save concert to database.
*/
public function save() : void {
global $wpdb;
$columns = array(
'wpgconcert_name' => $this->cname,
'venue' => $this->venue->id(),
'wpgconcert_date' => $this->cdate->format( 'c' ),
'wpgconcert_tickets' => $this->tickets,
'wpgconcert_event' => $this->eventlink,
'wpgconcert_status' => $this->status,
'wpgconcert_roles' => wp_json_encode( $this->roles ),
);
if ( null !== $this->id ) {
$res = $wpdb->update( $wpdb->prefix . 'giglogadmin_concerts', $columns, array( 'id' => $this->id ) );
} else {
$res = $wpdb->insert( $wpdb->prefix . 'giglogadmin_concerts', $columns );
}
if ( false === $res ) {
$wpdb->print_error( __METHOD__ );
} elseif ( null === $this->id ) {
$this->id = $wpdb->insert_id;
}
}
/**
* Return database id for concert.
*/
public function id() : int {
return $this->id ? $this->id : 0;
}
/**
* Return the concert "name".
*/
public function cname() : string {
return $this->cname ? $this->cname : '';
}
/**
* Return the concert venue.
*/
public function venue() {
return $this->venue;
}
/**
* Return the date of the concert.
*/
public function cdate() : DateTimeImmutable {
return $this->cdate;
}
/**
* Return the ticket url for the concert.
*/
public function tickets() {
return $this->tickets ? $this->tickets : '';
}
/**
* Return the event link for the concert.
*/
public function eventlink() {
return $this->eventlink ? $this->eventlink : '';
}
/**
* Return the status of the concert.
*/
public function status() : int {
return $this->status;
}
/**
* Set the status of the concert.
*
* @param int $new_status The new status for the concert.
*/
public function set_status( int $new_status ) {
$this->status = $new_status;
}
/**
* Return the roles defined for this concert.
*
* @return array<string, string>
*/
public function roles() : array {
return $this->roles ? $this->roles : array();
}
/**
* Assign a role for the concert to the given user.
*
* @param string $role The role to assign.
* @param string $username The user to assign the role to.
*/
public function assign_role( string $role, string $username ) : void {
$this->roles[ $role ] = $username;
}
/**
* Unassign any roles for this concert for the given user.
*
* @param string $username The user to remove from the roles.
*/
public function remove_user_from_roles( string $username ) : void {
$this->roles = array_filter( $this->roles, fn( $u) => $u != $username );
}
/**
* Return creation time of the concert object.
*/
public function created() : DateTimeImmutable {
return $this->created;
}
/**
* Return time of last update to this concert object.
*/
public function updated() : DateTimeImmutable {
return $this->updated;
}
}
}