Alimentalos/Backend

View on GitHub
relationships/src/Procedures/GeofenceProcedure.php

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
<?php


namespace Alimentalos\Relationships\Procedures;


use Alimentalos\Relationships\Events\GeofenceIn;
use Alimentalos\Relationships\Events\GeofenceOut;
use App\Models\Geofence;
use Grimzy\LaravelMysqlSpatial\Types\LineString;
use Grimzy\LaravelMysqlSpatial\Types\Point;
use Grimzy\LaravelMysqlSpatial\Types\Polygon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Query\Builder;

trait GeofenceProcedure
{
    /**
     * In status.
     */
    public $in_status = 1;

    /**
     * Still status.
     */
    public $still_status = 2;

    /**
     * Out status.
     */
    public $out_status = 3;

    /**
     * Check if location is using model geofence.
     *
     * @param Model $model
     * @param Model $location
     * @return void
     */
    public function checkLocationUsingModelGeofences(Model $model, Model $location)
    {
        $intersected_geofences = $model->geofences()->intersects('shape', $location->location)->get();
        foreach ($intersected_geofences as $geofence) {
            $this->checkIntersectedGeofence($model, $geofence, $location);
        }

        $disjoint_geofences = $model->geofences()->disjoint('shape', $location->location)->get();

        foreach ($disjoint_geofences as $geofence) {
            $this->checkDisjointedGeofence($model, $geofence, $location);
        }
    }

    /**
     * Create model accesses.
     *
     * @param Model $model
     * @param Geofence $geofence
     * @param Model $location
     * @return void
     */
    public function createAccess(Model $model, Geofence $geofence, Model $location)
    {
        $model->accesses()->create([
            'uuid' => uuid(),
            'geofence_uuid' => $geofence->uuid,
            'first_location_uuid' => $location->uuid,
            'last_location_uuid' => $location->uuid,
            'status' => $this->in_status,
        ]);
        event(new GeofenceIn($location, $geofence, $model));
    }

    /**
     * Get in and still in accesses query.
     *
     * @param Model $model
     * @param Geofence $geofence
     * @return Builder
     */
    public function inAndStillAccessQuery(Model $model, Geofence $geofence)
    {
        return $model->accesses()->where([
            ['accessible_id', $model->uuid],
            ['accessible_type', get_class($model)],
            ['geofence_uuid', $geofence->uuid],
            ['status', $this->still_status],
        ])->orWhere([
            ['accessible_id', $model->uuid],
            ['accessible_type', get_class($model)],
            ['geofence_uuid', $geofence->uuid],
            ['status', $this->in_status],
        ]);
    }

    /**
     * Update in or still access using the last known location and still status.
     *
     * @param Model $model
     * @param Geofence $geofence
     * @param Model $location
     */
    public function updateStillAccess(Model $model, Geofence $geofence, Model $location)
    {
        $this->inAndStillAccessQuery($model, $geofence)->update([
            'last_location_uuid' => $location->uuid,
            'status' => $this->still_status,
        ]);
    }

    /**
     * Update in or still access using the las known location and out status.
     *
     * @param Model $model
     * @param Geofence $geofence
     * @param Model $location
     */
    public function updateOutAccess(Model $model, Geofence $geofence, Model $location)
    {
        $this->inAndStillAccessQuery($model, $geofence)->update([
            'last_location_uuid' => $location->uuid,
            'status' => $this->out_status,
        ]);
        event(new GeofenceOut($location, $geofence, $model));
    }

    /**
     * Check intersected geofences of model location.
     *
     * @param Model $model
     * @param Geofence $geofence
     * @param Model $location
     */
    public function checkIntersectedGeofence(Model $model, Geofence $geofence, Model $location)
    {
        if ($this->inAndStillAccessQuery($model, $geofence)->exists()) {
            $this->updateStillAccess($model, $geofence, $location);
        } else {
            $this->createAccess($model, $geofence, $location);
        }
    }

    /**
     * Check disjointed geofences of model location.
     *
     * @param Model $model
     * @param Geofence $geofence
     * @param Model $location
     */
    public function checkDisjointedGeofence(Model $model, Geofence $geofence, Model $location)
    {
        if ($this->inAndStillAccessQuery($model, $geofence)->exists()) {
            $this->updateOutAccess($model, $geofence, $location);
        }
    }

    /**
     * Create points from shape input.
     *
     * @param $shape
     * @return array
     */
    public function createPointsFromShape($shape)
    {
        return array_map(function ($element) {
            return new Point($element['latitude'], $element['longitude']);
        }, $shape);
    }

    /**
     * Create geofence instance.
     *
     * @return Geofence
     */
    public function createInstance()
    {
        // Instance & Attributes
        $geofence = new Geofence();
        $geofence->uuid = uuid();
        $geofence->name = input('name');
        $geofence->user_uuid = authenticated()->uuid;

        // Colors
        foreach(Geofence::getColors() as $attribute) {
            $geofence->{$attribute} = fill($attribute, null);
        }

        // Shape
        $shape = $this->createPointsFromShape(input('shape'));
        $geofence->shape = new Polygon([new LineString($shape)]);

        // Visibility
        $geofence->is_public = input('is_public');

        // Create
        $geofence->save();

        upload()->checkPhoto($geofence);
        upload()->checkMarker($geofence);

        return $geofence;
    }

    /**
     * Update geofence instance.
     *
     * @param Geofence $geofence
     * @return Geofence
     */
    public function updateInstance(Geofence $geofence)
    {
        // Check photo uploaded
        upload()->checkPhoto($geofence);

        // Attributes
        foreach (['name', 'is_public'] as $item) {
            $geofence->{$item} = fill($item, $geofence->{$item});
        }

        // Shape
        $geofence->shape = new Polygon(
            [new LineString(
                array_map(function ($element) {
                    return new Point($element['latitude'], $element['longitude']);
                }, input('shape'))
            )]
        );

        // Marker
        upload()->checkMarker($geofence);

        // Colors
        foreach(Geofence::getColors() as $attribute) {
            $geofence->{$attribute} = fill($attribute, $geofence->{$attribute});
        }

        // Update
        $geofence->save();
        return $geofence;
    }
}