ameliaikeda/monzo-php

View on GitHub
src/Models/Model.php

Summary

Maintainability
A
25 mins
Test Coverage
<?php

namespace Amelia\Monzo\Models;

use ArrayAccess;
use Carbon\Carbon;
use JsonSerializable;
use Amelia\Monzo\Monzo;
use Illuminate\Support\Str;
use Illuminate\Contracts\Support\Jsonable;
use Illuminate\Contracts\Support\Arrayable;

class Model implements Jsonable, Arrayable, JsonSerializable, ArrayAccess
{
    /**
     * The original values given to this model.
     *
     * @var array
     */
    protected $original = [];

    /**
     * An array of attributes on this model.
     *
     * @var array
     */
    protected $attributes = [];

    /**
     * Attributes to append to this model's json output.
     *
     * @var array
     */
    protected $appends = [];

    /**
     * An array of cast types for this model.
     *
     * As an improvement over laravel, you can cast to models, too.
     *
     * @var array
     */
    protected $casts = [];

    /**
     * A static monzo instance.
     *
     * @var \Amelia\Monzo\Monzo
     */
    protected $monzo;

    /**
     * Base Monzo model constructor.
     *
     * @param array $attributes
     * @param \Amelia\Monzo\Monzo|null $monzo
     */
    public function __construct(array $attributes = [], Monzo $monzo = null)
    {
        $this->setAttributes($attributes);

        // dev UX test: make models API-aware?
        // bonus: it's already authed from the previous call.
        if ($monzo !== null) {
            $this->monzo = $monzo;
        }
    }

    /**
     * Get the instance as an array.
     *
     * @return array
     */
    public function toArray()
    {
        $attributes = $this->attributes;

        foreach ($this->appends as $append) {
            $method = 'get' . Str::studly($append) . 'Attribute';

            if (method_exists($this, $method)) {
                $attributes[$append] = $this->$method();
            }
        }

        return $attributes;
    }

    /**
     * Get an array suitable for JSON encoding.
     *
     * @return array
     */
    public function toJsonArray()
    {
        return collect($this->toArray())->map(function ($value) {
            return $value instanceof Carbon
                ? $value->format('Y-m-d\TH:i:s.uP')
                : $value;
        })->all();
    }

    /**
     * Convert the object to its JSON representation.
     *
     * @param  int $options
     * @return string
     */
    public function toJson($options = 0)
    {
        return json_encode($this->toJsonArray(), $options);
    }

    /**
     * Specify data which should be serialized to JSON.
     * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
     * @return mixed data which can be serialized by <b>json_encode</b>,
     * which is a value of any type other than a resource.
     * @since 5.4.0
     */
    public function jsonSerialize()
    {
        return $this->toJsonArray();
    }

    /**
     * Set attributes on this model.
     *
     * @param array $attributes
     * @return void
     */
    public function setAttributes(array $attributes)
    {
        $this->original = $attributes;

        foreach ($attributes as $key => $value) {
            $this->attributes[$key] = $this->cast($key, $value);
        }
    }

    /**
     * Cast a value explicitly to a type defined in {@see static::$casts}.
     *
     * @param string $key
     * @param mixed $value
     * @return mixed
     */
    public function cast(string $key, $value)
    {
        if (! array_key_exists($key, $this->casts) || $value === null) {
            return $value;
        }

        $cast = $this->casts[$key];
        $collection = false;

        if (starts_with($cast, 'collection:') && is_array($value)) {
            $collection = true;
            $cast = substr($cast, 10);
        }

        if (class_exists($cast) && is_array($value)) {
            return $this->castClass($cast, $value, $collection);
        }

        switch ($cast) {
            case 'date':
                return Carbon::parse($value);

            default:
                return $value;
        }
    }

    /**
     * Cast to a class (aka a relation).
     *
     * @param string $cast
     * @param array|mixed $value
     * @param bool $collection
     * @return \Amelia\Monzo\Models\Model|\Illuminate\Support\Collection
     */
    protected function castClass(string $cast, $value, bool $collection = false)
    {
        if ($collection) {
            return collect($value)->map(function (array $item) use ($cast) {
                return new $cast($item);
            });
        }

        return new $cast($value);
    }

    /**
     * Get an attribute on this model.
     *
     * @param string $key
     * @return mixed|null
     */
    public function getAttribute(string $key)
    {
        $method = 'get' . Str::studly($key) . 'Attribute';

        if (method_exists($this, $method)) {
            return $this->$method();
        }

        return $this->attributes[$key] ?? null;
    }

    /**
     * Dynamically get an attribute on this model.
     *
     * @param string $key
     * @return mixed|null
     */
    public function __get(string $key)
    {
        return $this->getAttribute($key);
    }

    /**
     * Debug information for var_dump/dd.
     *
     * @return array
     */
    public function __debugInfo()
    {
        return $this->toJsonArray();
    }

    /**
     * Whether a offset exists.
     * @link http://php.net/manual/en/arrayaccess.offsetexists.php
     * @param mixed $offset <p>
     * An offset to check for.
     * </p>
     * @return bool true on success or false on failure.
     * </p>
     * <p>
     * The return value will be casted to boolean if non-boolean was returned.
     * @since 5.0.0
     */
    public function offsetExists($offset)
    {
        return array_key_exists($offset, $this->attributes);
    }

    /**
     * Offset to retrieve.
     * @link http://php.net/manual/en/arrayaccess.offsetget.php
     * @param mixed $offset <p>
     * The offset to retrieve.
     * </p>
     * @return mixed Can return all value types.
     * @since 5.0.0
     */
    public function offsetGet($offset)
    {
        return $this->getAttribute($offset);
    }

    /**
     * Offset to set.
     * @link http://php.net/manual/en/arrayaccess.offsetset.php
     * @param mixed $offset <p>
     * The offset to assign the value to.
     * </p>
     * @param mixed $value <p>
     * The value to set.
     * </p>
     * @return void
     * @since 5.0.0
     */
    public function offsetSet($offset, $value)
    {
        //
    }

    /**
     * Offset to unset.
     * @link http://php.net/manual/en/arrayaccess.offsetunset.php
     * @param mixed $offset <p>
     * The offset to unset.
     * </p>
     * @return void
     * @since 5.0.0
     */
    public function offsetUnset($offset)
    {
        //
    }
}