Newer
Older
smart-home-server / server / SHServ / Controllers / AreasRESTAPIController.php
<?php

namespace SHServ\Controllers;

use \SHServ\Models\Areas;
use \SHServ\Entities\Area;

/**
 * Контроллер реализующий REST API для работы с абстрактным описанием физических областей.
 * Подчёркиваю, это Areas, а не Spaces. Реализована логика работы именно с физическим областями, а не абстрактным пространством.
 */
class AreasRESTAPIController extends \SHServ\Middleware\Controller {
	/**
	 * Получить список пространств в системе. Всех или у родительского пространства
	 * @param  Int $area_id Area ID
	 * @return String JSON. Вернёт массив областей в поле `areas` и  количество в поле `total`
	 */
	public function areas_list($area_id = 0) {
		$areas_model = new Areas();

		if(!$area_id) {
			$areas = $areas_model -> get_all();
		} else {
			$parent_area = new Area(intval($area_id));
			if(!$parent_area) {
				return $this -> utils() -> response_error("parent_area_not_found");
			}

			$areas = $parent_area -> get_inner_areas();
		}

		$response = [];

		foreach($areas as $i => $area) {
			$response[] = $area -> to_array();
		}

		return $this -> utils() -> response_success([
			"areas" => $response,
			"total" => count($response)
		]);
	}

	/**
	 * Добавить новую физическую область
	 * @param  String $type         Произвольный тип области. Это может быть например комната, этаж, здание, или двор. Ограничений нет, но тип должен описывать именно физическую область.
	 * @param  String $alias        Псевдоним для обращения к области из кода. Обязательно должен быть уникальным.
	 * @param  String $display_name Отображаемое название/имя области
	 * @return String               JSON. Возвращает поля созданной `area` и `alias` или ошибку.
	 */
	public function new_area($type, $alias, $display_name) {
		$areas_model = new Areas();

		if(!$areas_model -> alias_is_uniq($alias)) {
			return $this -> utils() -> response_error("alias_already_exists", ["alias"]);
		}

		if(!strlen($type)) {
			return $this -> utils() -> response_error("empty_field", ["type"]);
		}

		if(!strlen($display_name)) {
			return $this -> utils() -> response_error("empty_field", ["display_name"]);
		}

		$area = $areas_model -> create_new_area($type, $alias, $display_name);

		if(!$area) {
			return $this -> utils() -> response_error("area_not_exists");
		}

		return $this -> utils() -> response_success([
			"alias" => $area -> alias,
			"area" => $area -> to_array()
		]);
	}

	/**
	 * Удалить область. При этом все помещённые в неё устройства и child области будут отвязаны
	 * @param  Int $area_id Area ID
	 * @return String       JSON. Вернёт ошибку или положительный результат
	 */
	public function remove_area($area_id) {
		if($area_id != intval($area_id) or intval($area_id) <= 1) {
			return $this -> utils() -> response_error("invalid_id", ["area_id"]);
		}

		$area = new Area(intval($area_id));

		if(!$area) {
			return $this -> utils() -> response_error("area_not_exists");
		}

		if(!$area -> remove()) {
			return $this -> utils() -> response_error("undefined_error");
		}

		return $this -> utils() -> response_success();
	}

	/**
	 * Поместить/вложить одну область в другую. Например комнату можна поместить в этаж, этаж в здание, а здание в глобальную область типа "дом/комплекс/т.п.".
	 * @param  Int $target_area_id   Area ID целевой области, которую хотим поместить
	 * @param  Int $place_in_area_id Area ID родительской области в которую хотим поместить целевую область
	 * @return String                JSON. Вернёт ошибку или положительный результат
	 */
	public function place_in_area($target_area_id, $place_in_area_id) {
		if($target_area_id != intval($target_area_id) or intval($target_area_id) <= 1) {
			return $this -> utils() -> response_error("invalid_id", ["target_area_id"]);
		}

		if($place_in_area_id != intval($place_in_area_id) or intval($place_in_area_id) <= 1) {
			return $this -> utils() -> response_error("invalid_id", ["place_in_area_id"]);
		}

		$target_area = new Area(intval($target_area_id));
		$place_area = new Area(intval($place_in_area_id));

		if(!$target_area or !$place_area) {
			return $this -> utils() -> response_error("area_not_exists");
		}

		if(!$target_area -> place_in_area($place_area)) {
			return $this -> utils() -> response_error("undefined_error");
		}

		return $this -> utils() -> response_success();
	}

	/**
	 * Переименовать область
	 * @param  Int $area_id         Area ID области, которую переименовываем
	 * @param  String $display_name Новое название/имя области
	 * @return String               JSON. Возвращает поля `area` с данными об изменённой области и `alias`.
	 */
	public function update_display_name($area_id, $display_name) {
		if($area_id != intval($area_id) or intval($area_id) <= 1) {
			return $this -> utils() -> response_error("invalid_id");
		}

		if(!strlen($display_name)) {
			return $this -> utils() -> response_error("empty_field", ["display_name"]);
		}

		$area = new Area(intval($area_id));

		if(!$area) {
			return $this -> utils() -> response_error("area_not_exists");
		}

		$area -> display_name = $display_name;

		return $area -> update()
			? $this -> utils() -> response_success([ 
					"alias" => $area -> alias,
					"area" => $area -> to_array()
				])
			: $this -> utils() -> response_error("undefined_error");
	}

	/**
	 * Измениеть псевдоним для обращения к области из кода.
	 * ОСТОРОЖНО! если область уже учавствует в написанных скриптах, это сломает работу скриптов.
	 * @param  Int $area_id   Area ID
	 * @param  String $new_alias Новый уникальный псевдоним области
	 * @return String            JSON. Возвращает поля `area` с данными об изменённой области и `alias`. 
	 */
	public function update_alias($area_id, $new_alias) {
		if($area_id != intval($area_id) or intval($area_id) <= 1) {
			return $this -> utils() -> response_error("invalid_id", ["area_id"]);
		}

		if(!strlen($new_alias)) {
			return $this -> utils() -> response_error("empty_field", ["new_alias"]);
		}

		$areas_model = new Areas();

		if(!$areas_model -> alias_is_uniq($new_alias)) {
			return $this -> utils() -> response_error("alias_already_exists", ["alias"]);
		}

		$area = new Area(intval($area_id));

		if(!$area) {
			return $this -> utils() -> response_error("area_not_exists");
		}

		$area -> alias = $new_alias;

		return $area -> update()
			? $this -> utils() -> response_success([ 
					"alias" => $area -> alias,
					"area" => $area -> to_array() 
				])
			: $this -> utils() -> response_error("undefined_error");
	}

	/**
	 * Перезагрузить все устройства, что помещены в указанную область.
	 * @param  Int $area_id Area ID области в которой нужно перезагрузить все устройства.
	 * @return String       JSON. При положительном ответе вернётся `total` - количество устройств, что были перезагружены, 
	 * и `results` - ассоциативный массив с ключами вида `area_alias:device_alias` и телом ответа от устройства в качестве значения.
	 */
	public function reboot_devices($area_id = 0) {
		$area = new Area(intval($area_id));

		if(!$area) {
			return $this -> utils() -> response_error("area_not_exists");
		}

		$devices = $area -> get_inner_devices(true);

		$results = [];
		foreach($devices as $device) {
			$results["{$area -> alias}:{$device -> alias}"] = $device -> device_api() -> reboot();
		}

		return $this -> utils() -> response_success([
			"results" => $results,
			"total" => count($results)
		]);
	}

	/**
	 * Получить список устройств, что помещены в указанную область.
	 * @param  Int $area_id Area ID
	 * @return String       JSON. Возвращает массив устройств в поле `devices` и количество устрйоств в поле `total`
	 */
	public function devices_list($area_id) {
		if($area_id != intval($area_id) or intval($area_id) <= 1) {
			return $this -> utils() -> response_error("invalid_id", ["area_id"]);
		}

		$area = new Area(intval($area_id));

		if(!$area) {
			return $this -> utils() -> response_error("area_not_exists");
		}

		$devices = $area -> get_inner_devices(true);

		return $this -> utils() -> response_success([
			"devices" => array_map(function($device) {
				return $device -> to_array();
			}, $devices),
			"total" => count($devices)
		]);
	}

	/**
	 * Отвязать указанную область от области в которую она почещена. Устройства, что помещены в эту область в ней останутся.
	 * @param  Int $area_id Area ID
	 * @return String       JSON. Вернётся ошибка или положительный ответ.
	 */
	public function unassign_from_area($area_id) {
		if($area_id != intval($area_id) or intval($area_id) <= 1) {
			return $this -> utils() -> response_error("invalid_id", ["area_id"]);
		}

		$area = new Area(intval($area_id));

		if(!$area) {
			return $this -> utils() -> response_error("area_not_exists");
		}

		return $area -> place_in_area_by_id(0)
			? $this -> utils() -> response_success()
			: $this -> utils() -> response_error("undefined_error");
	}

	/**
	 * Получить список существующих в БД пользовательских типов областей. Это может быть полезно для выведения подсказок пользователю при добавлении новой области, 
	 * чтоб помогать не плодить множества одинаковых по смыслу, но разных по названию, типов.
	 * @return String JSON Вернёт поле `types` с массивом типов
	 */
	public function exists_types() {
		return $this -> utils() -> response_success([
			"types" => (new Areas()) -> get_exists_types()
		]);
	}
}