Как стать автором
Обновить

Ускоряем разработку на PHP с помощью перегрузки

Время на прочтение2 мин
Количество просмотров3.8K

Проблема


Часто приходится писать так (примеры кода на Yii, но подход можно применить к любому коду):
$model = new User();
$model->name = 'Вася';
if (!$model->save())
    throw new RuntimeException('Can not save!');

Или так:
$model = User::model()->find();
if (!$model)
    throw new CHttpException(404, 'User not found!');

Кеширование:
$dependency = new \caching\TagDependency('Post');
$posts = Post::model()->cache(1000, $dependency)->findAll();

Транзакции:
$trx = $this->getDbConnection()->beginTransaction();
try {
    if (!$user->makePayment())
        throw new \RuntimeException('Can not complete!');

    $trx->commit();
} catch (\Exception $e) {
    $trx->rollback();

    throw $e;
}


Слишком много кода!

Решение


Вышеприведенные куски кода можно сократить до:
Возбуждаем исключение в случае неудачного результата:
$model->saveException();
User::model()->findException();
Post::model()->findAllCached();


Оборачиваем метод в транзакцию:
$user->makePaymentTrx();


Также возможны комбинации:
User::model()->findTrxCached()




Как это реализовать?


На Yii создаем свой класс ActiveRecord и наследуем от него свои модели:
class ActiveRecord extends CActiveRecord
{
	public function __call($name, $args)
	{
		if (preg_match('/^(.+)(cached|exception|trx)$/i', $name, $matches)) {
			switch (strtolower($matches[2])) {
				case 'cached':
					return $this->cachedMethod($matches[1], $args);
				case 'exception':
					return $this->exceptionMethod($matches[1], $args);
				case 'trx':
					return $this->trxMethod($matches[1], $args);
			} 			
		}

		return parent::__call($name, $args);
	}

	public function trxMethod($method, $args)
	{
		$trx = $this->getDbConnection()->beginTransaction();
		try {
			$value = call_user_func_array(array($this, $method), $args);

			$trx->commit();
		} catch (\Exception $e) {
			$trx->rollback();

			throw $e;
		}

		return $value;
	}

	public function exceptionMethod($method, $args)
	{	
		$value = call_user_func_array(array($this, $method), $args);
		
		if (!$value)
			throw new Exception('False result!');
		
		return $value;
	}
	
	public function cachedMethod($method, $args)
	{
		$key = get_class($this) . $method . serialize($this->getPrimaryKey()) .  serialize($args) . serialize($this->getDbCriteria());
		$key = md5($key);

		$value = \Yii::app()->cache->get($key);

		if ($value === false) {
			$value = call_user_func_array(array($this, $method), $args);
			\Yii::app()->cache->set($key, $value, 0, new \caching\TagDependency(get_class($this)));
		} else {
			//reset scope as in find*() methods
			$this->resetScope();
		}

		return $value;
	}
}


Основа метода — регулярное выражение:
preg_match('/^(.+)(cached|exception|trx)$/i', $name, $matches)


Вы можете добавить сюда свои суффиксы.

Преимущества:


  • Рутинные операции занимают гораздо меньше места в коде.
  • Быстрее и проще писать код.


Недостатки:


  • IDE не видит такие методы или их надо прописывать вручную, что не очень удобно.
Теги:
Хабы:
Всего голосов 37: ↑8 и ↓29-21
Комментарии11

Публикации

Истории

Работа

PHP программист
150 вакансий

Ближайшие события