railpage/railpagecore

View on GitHub
lib/Events/Event.php

Summary

Maintainability
F
5 days
Test Coverage
<?php

/**
 * Railpage Events module
 * @since Version 3.8.7
 * @package Railpage
 * @author Michael Greenhill
 */

namespace Railpage\Events;

use Railpage\Users\User;
use Railpage\Users\Factory as UserFactory;
use Railpage\AppCore;
use Railpage\Organisations\Factory as OrganisationsFactory;
use Railpage\Organisations\Organisation;
use Railpage\Place;
use Railpage\Module;
use Railpage\Debug;
use Railpage\Url;
use DateTime;
use Exception;
use stdClass;
use Railpage\Images\Images;
use Railpage\ContentUtility;

/**
 * Event class
 * @since Version 3.8.7
 */

class Event extends AppCore {
    
    /**
     * Registry key
     * @since Version 3.9.1
     * @const string REGISTRY_KEY
     */
    
    const REGISTRY_KEY = "railpage:events.event=%d";
    
    /**
     * Memcached/Redis cache key
     * @since Version 3.9.1
     * @const string CACHE_KEY
     */
    
    const CACHE_KEY = "railpage:events.event=%d";
    
    /**
     * Event ID
     * @var int $id
     * @since Version 3.8.7
     */
     
    public $id;
    
    /**
     * Event title
     * @var string $title
     * @since Version 3.8.7
     */
    
    public $title;
    
    /**
     * Descriptive text
     * @var string $desc
     * @since Version 3.8.7
     */
    
    public $desc;
    
    /**
     * Meta data
     * @var array $meta An array of extra data for this event, such as ticket purchasing websites
     * @since Version 3.8.7
     */
    
    public $meta;
    
    /**
     * Event category
     * @var \Railpage\Events\EventCategory $Category The event category (tours, open days, etc) that this event is filed under
     * @since Version 3.8.7
     */
    
    public $Category;
    
    /**
     * Place
     * @var \Railpage\Place $Place The geographic place (latitude & longitude) of this event
     * @since Version 3.8.7
     */
    
    public $Place;
    
    /**
     * Organisation associated with this event
     * @var \Railpage\Organisations\Organisation $Organisation The organisation hosting/running/assocated with this event
     * @since Version 3.8.7
     */
    
    public $Organisation;
    
    /**
     * Event url
     * @var string $url The URL relative to the site root of this event - eg
     * @since Version 3.8.7
     */
    
    public $url;
    
    /**
     * Flickr tag
     * @since Version 3.8.7
     * @var string $flickr_tag A tag which when applied to Flickr photos will become associated with this event
     */
    
    public $flickr_tag;
    
    /**
     * Event status
     * @since Version 3.9.1
     * @var int $status
     */
    
    public $status;
    
    /**
     * Constructor
     * @since Version 3.8.7
     * @param int $id The ID of the event
     * @uses \Railpage\Events\EventCategory The category which this event is filed under
     * @uses \Railpage\Organisations\Organisation The organisation associated with this event
     * @uses \Railpage\Place The geographic place (latitude & longitude) of this event
     */
    
    public function __construct($id = null) {
        
        parent::__construct();
        
        Debug::RecordInstance(); 
        
        $timer = Debug::getTimer(); 
        
        $this->Module = new Module("events");
        $this->namespace = $this->Module->namespace;
        
        if ($id != null) {
            $this->populate($id); 
        }
        
        Debug::logEvent(__METHOD__, $timer); 
        
    }
    
    /**
     * Load this object
     * @since Version 3.9.1
     * @return array
     * @param int|string $id
     */
    
    private function load($id) {
        
        if (!filter_var($id, FILTER_VALIDATE_INT)) {
            $id = $this->db->fetchOne("SELECT id FROM event WHERE slug = ?", $id); 
        }
        
        if (!$id = filter_var($id, FILTER_VALIDATE_INT)) {
            return;
        }
        
        $this->id = $id;
        $this->mckey = sprintf("railpage:events.event=%d", $this->id);
        $query = "SELECT * FROM event WHERE id = ?";
        
        if (!$row = $this->Memcached->fetch($this->mckey)) {
            $row = $this->db->fetchRow($query, $this->id);
            Debug::logEvent(__METHOD__ . " - fetched from SQL"); 
            
            $this->Memcached->save($this->mckey, $row);
        }
        
        return $row;

    }
    
    /**
     * Populate this object
     * @since Version 3.9.1
     * @return void
     * @param int|string $id
     */
    
    private function populate($id) {
        
        $row = $this->load($id); 
        
        if (!isset($row) || !is_array($row)) {
            return;
        }
        
        $this->title = $row['title'];
        $this->desc = $row['description']; 
        $this->meta = json_decode($row['meta'], true);
        $this->slug = $row['slug'];
        $this->status = isset($row['status']) ? $row['status'] : Events::STATUS_APPROVED;
        
        if (!isset($row['user_id'])) {
            $row['user_id'] = 45;
        }
        
        $this->setAuthor(UserFactory::CreateUser($row['user_id']));
        
        $this->flickr_tag = "railpage:event=" . $this->id;
        
        if (filter_var($row['category_id'], FILTER_VALIDATE_INT)) {
            $this->Category = Factory::CreateEventCategory($row['category_id']);
        }
        
        if (filter_var($row['organisation_id'], FILTER_VALIDATE_INT)) {
            $this->Organisation = OrganisationsFactory::CreateOrganisation($row['organisation_id']);
        }
        
        if (!empty($row['lat']) && round($row['lat'], 3) != "0.000" && !empty($row['lon']) && round($row['lon'], 3) != "0.000") {
            $this->Place = Place::Factory($row['lat'], $row['lon']);
        }
        
        $this->createUrls();
        $this->Templates = new stdClass();
        $this->Templates->view = sprintf("%s/event.tpl", $this->Module->Paths->html);
    }
    
    /**
     * Validate this event
     * @since Version 3.8.7
     * @return boolean
     * @throws Exception if $this->title is empty
     * @throws Exception if $this->desc is empty
     * @throws Exception if $this->Category is not an instance of Railpage\Events\EventCategory
     */
    
    private function validate() {
        if (empty($this->title)) {
            throw new Exception("Validation failed for event. Title cannot be empty");
        }
        
        if (empty($this->desc)) {
            throw new Exception("Validation failed for event. Description cannot be empty");
        }
        
        if (!$this->Category instanceof EventCategory) {
            throw new Exception("Validation failed for event. Event must have a category!");
        }
        
        if (!isset($this->slug) || empty($this->slug)) {
            $this->createSlug();
        }
        
        if (!filter_var($this->status)) {
            $this->status = Events::STATUS_UNAPPROVED;
        }
        
        if (!$this->Author instanceof User) {
            throw new Exception("A valid user object must be set (hint: Event::setAuthor()");
        }
        
        $this->title = ContentUtility::FormatTitle($this->title);
        
        return true;
    }
    
    /**
     * Commit changes to this event
     * @since Version 3.8.7
     * @return boolean ZendFramework\Db will throw an exception if there's a fault
     */
    
    public function commit() {
        $this->validate(); 
        
        $data = array(
            "title" => $this->title,
            "description" => $this->desc,
            "meta" => json_encode($this->meta),
            "category_id" => $this->Category->id,
            "slug" => $this->slug,
            "status" => $this->status,
            "user_id" => $this->Author->id,
            "lat" => "",
            "lon" => "",
            "organisation_id" => 0
        );
        
        if ($this->Organisation instanceof Organisation) {
            $data['organisation_id'] = $this->Organisation->id;
        }
        
        if ($this->Place instanceof Place) {
            $data['lat'] = $this->Place->lat;
            $data['lon'] = $this->Place->lon;
        }
        
        if (filter_var($this->id)) {
            $where = array(
                "id = ?" => $this->id
            );
            
            $this->db->update("event", $data, $where); 
        
            $this->Redis->delete(sprintf(self::CACHE_KEY, $this->id)); 
            
            if (isset($this->mckey)) {
                $this->Memcached->delete($this->mckey);
            }
            
            return true;
            
        }
        
        $this->db->insert("event", $data);
        $this->id = $this->db->lastInsertId(); 
        
        $this->createUrls();
        
        return true;
    }
    
    /**
     * Get event dates (instances)
     * @return array
     * @param DateTime $dateFrom DateTime instance representing the date to limit searches from 
     * @param DateTime $dateTo DateTme instance representing the date to limit searches to
     * @param $status Only show dates with this status flag
     */
    
    public function getDates(DateTime $dateFrom, DateTime $dateTo = NULL, $status = Events::STATUS_APPROVED) {
        
        if (RP_DEBUG) {
            global $site_debug;
            $debug_timer_start = microtime(true);
        }
        
        $query = "SELECT id FROM event_dates WHERE event_id = ? AND status = ?";
        
        $params = array(
            $this->id,
            $status
        );
        
        if ($dateFrom instanceof DateTime) {
            $query .= " AND date >= ?";
            $params[] = $dateFrom->format("Y-m-d");
        }
        
        if ($dateTo instanceof DateTime) {
            $query .= " AND date <= ?";
            $params[] = $dateTo->format("Y-m-d");
        }
        
        $rs = $this->db->fetchAll($query, $params);
        
        if (RP_DEBUG) {
            $site_debug[] = __CLASS__ . "::" . __METHOD__ . " completed in " . round(microtime(true) - $debug_timer_start, 5) . "s";
        }
        
        return $rs;
    }
    
    /**
     * Find an instance of this event on a given date
     * @return mixed
     * @param DateTime $dateObject DateTime instance to search for
     * @throws Exception if $dateObject is not a valid instance of DateTime
     */
    
    public function findDate(DateTime $dateObject) {
        
        if (RP_DEBUG) {
            global $site_debug;
            $debug_timer_start = microtime(true);
        }
        
        $query = "SELECT id FROM event_dates WHERE event_id = ? AND date = ? AND status = ?";
        
        if (!$dateObject instanceof DateTime) {
            throw new Exception("Cannot find dates for this event - no DateTime object given");
        }
        
        if (RP_DEBUG) {
            $site_debug[] = __CLASS__ . "::" . __METHOD__ . " completed in " . round(microtime(true) - $debug_timer_start, 5) . "s";
        }
        
        $where = array(
            $this->id, 
            $dateObject->format("Y-m-d"),
            Events::STATUS_APPROVED
        );
        
        return $this->db->fetchAll($query, $where);
    }
    
    /**
     * Approve this event
     * @since Version 3.9.1
     * @return \Railpage\Events\Event
     */
    
    public function approve() {
        $this->status = Events::STATUS_APPROVED;
        $this->commit(); 
        
        return $this;
    }
    
    /**
     * Reject this event
     * @since Version 3.8.7
     * @return boolean
     */
    
    public function reject() {
        if (!filter_var($this->id, FILTER_VALIDATE_INT)) {
            throw new Exception("Cannot reject event - no event ID specified");
        }
        
        $where = array(
            "event_id = ?" => $this->id
        );
        
        $this->db->delete("event_dates", $where);
        $where = array(
            "id = ?" => $this->id
        );
        
        $this->db->delete("event", $where);
        
        return true;
            
    }
    
    /**
     * Create a URL slug
     * @since Version 3.8.7
     */
    
    private function createSlug() {
        
        $proposal = substr(create_slug($this->title), 0, 32);
        
        $result = $this->db->fetchAll("SELECT id FROM event WHERE slug = ?", $proposal); 
        
        if (count($result)) {
            $proposal .= count($result);
        }
        
        $this->slug = $proposal;
        $this->commit();
        
    }
    
    /**
     * Create URLs
     * @since Version 3.8.7
     * @return $this
     */
    
    private function createUrls() {
        if (empty($this->slug)) {
            $this->createSlug(); 
        }
        
        $this->url = new Url(sprintf("%s/%s", $this->Module->url, $this->slug));
        $this->url->edit = sprintf("%s?mode=event.edit&event_id=%d", $this->Module->url, $this->id);
        $this->url->delete = sprintf("%s?mode=event.reject&event_id=%d", $this->Module->url, $this->id);
        $this->url->reject = $this->url->delete;
        $this->url->approve = sprintf("%s?mode=event.approve&event_id=%d", $this->Module->url, $this->id);
        $this->url->publish = $this->url->approve; 
        
        if (isset($this->meta['website']) && !empty($this->meta['website'])) {
            $this->url->website = $this->meta['website'];
        }
        
        if (isset($this->meta['tickets']) && !empty($this->meta['tickets'])) {
            $this->url->tickets = $this->meta['tickets'];
        }
    }
    
    /**
     * Create an associative array representing this object
     * @since Version 3.9.1
     * @return array
     */
    
    public function getArray() {
        $array = array(
            "id" => $this->id,
            "title" => $this->title,
            "description" => $this->desc,
            "status" => array(
                "id" => $this->status,
                "name" => $this->status == Events::STATUS_APPROVED ? "Approved" : "Unapproved"
            ),
            "url" => $this->url->getURLs(),
            "category" => array(
                "id" => $this->Category->id,
                "name" => $this->Category->name,
                "url" => $this->Category->url->getURLs()
            ),
            "place" => array(
                "lat" => 0,
                "lon" => 0
            ),
            "author" => array(
                "id" => $this->Author->id,
                "username" => $this->Author->username,
                "url" => $this->Author->url->getURLs()
            )
        );
        
        if ($this->Organisation instanceof Organisation) {
            $array['organisation'] = $this->Organisation->getArray();
        }
        
        if ($this->Place instanceof Place) {
            $array['place'] = array(
                "lat" => $this->Place->lat,
                "lon" => $this->Place->lon
            );
        }
        
        return $array;
    }
    
    /**
     * Get the street address of this event if applicable
     * @since Version 3.10.0
     * @return string
     */
    
    public function getAddress() {
        
        if (!empty($this->meta['address'])) {
            return $this->meta['address'];
        }
        
        if (!$this->Place instanceof Place) {
            return;
        }
        
        if ($this->Place instanceof Place) {
            
            $this->meta['address'] = $this->Place->getAddress(); 
            $this->commit(); 
            
            return $this->meta['address'];
            
        }
        
    }
    
    /**
     * Get the cover photo for this event
     * @since Version 3.10.0
     * @return false|array
     */
    
    public function getCoverPhoto() {
        
        if (!isset($this->meta['coverphoto']) || empty($this->meta['coverphoto'])) {
            return false;
        }
        
        if ($CoverPhoto = (new Images)->getImageFromUrl($this->meta['coverphoto'], Images::OPT_NOPLACE)) {
            return $CoverPhoto->getArray();
        }
        
        return false;
        
    }
}