<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>SimpleCoding.org &#187; Yii</title>
	<atom:link href="http://www.simplecoding.org/category/yii/feed" rel="self" type="application/rss+xml" />
	<link>http://www.simplecoding.org</link>
	<description>Блог о программировании</description>
	<lastBuildDate>Fri, 27 Jan 2012 18:27:45 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=</generator>
		<item>
		<title>Yii фреймворк: создание XML-RPC сервера</title>
		<link>http://www.simplecoding.org/yii-frejmvork-sozdanie-xml-rpc-servera.html</link>
		<comments>http://www.simplecoding.org/yii-frejmvork-sozdanie-xml-rpc-servera.html#comments</comments>
		<pubDate>Sat, 24 Dec 2011 20:15:06 +0000</pubDate>
		<dc:creator>Владимир</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web разработка]]></category>
		<category><![CDATA[Yii]]></category>

		<guid isPermaLink="false">http://www.simplecoding.org/?p=1247</guid>
		<description><![CDATA[На мой взгляд, XML-RPC протокол является одним из наиболее удобных способов передачи данных между Интернет-ресурсами. Можно, конечно, поспорить, но на изучение XML-RPC требуется минимальное количество времени, есть множество готовых библиотек, да и используется он очень широко. Поэтому я никогда не понимал, почему разработчики отличного фреймворка Yii решили не включать библиотеку для работы с XML-RPC в [...]]]></description>
			<content:encoded><![CDATA[<div id="attachment_1021" class="wp-caption alignnone" style="width: 272px"><img src="http://www.simplecoding.org/wp-content/uploads/2010/03/yii_xml_rpc.png" alt="yii xml rpc" title="yii xml rpc" width="262" height="189" style="float:left" class="size-full wp-image-1021" /><p class="wp-caption-text"> </p></div>
<p>На мой взгляд, <strong>XML-RPC протокол</strong> является одним из наиболее удобных способов передачи данных между Интернет-ресурсами. Можно, конечно, поспорить, но на изучение XML-RPC требуется минимальное количество времени, есть множество готовых библиотек, да и используется он очень широко. Поэтому я никогда не понимал, почему разработчики отличного <a href="http://www.yiiframework.com">фреймворка Yii</a> решили не включать библиотеку для работы с XML-RPC в дистрибутив. Хотя, возможно, это вопрос времени <img src='http://www.simplecoding.org/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Тем не менее, работать нужно уже сейчас, и в этой статье мы разберём, как решить проблему с помощью сторонних библиотек.</p>
<p><em>Примечание</em>. Если вас интересует создание XML-RPC клиента, почитайте статью <a href="http://www.simplecoding.org/xml-rpc-i-yii-frejmvork.html">XML-RPC и Yii фреймворк</a>.</p>
<h2>Немного теории.</h2>
<p>Мы можем использовать два основных подхода при создании <strong>XML-RPC сервера</strong>.</p>
<p><strong>1) Отдельный скрипт.</strong> В этом случае создаём файл с именем вроде xmlrpc.php и помещаем его в корень сайта. В нём будет находиться код обработки XML-RPC запросов. Такой подход используется, например, в WordPress. Достоинство в том, что вам не нужно вносить никаких изменений в код вашего проекта, т.е. XML-RPC интерфейс получается независимым. С другой стороны, независимость приводит к тому, что вам будет гораздо сложнее работать со встроенными библиотеками фреймворка.</p>
<p><strong>2) Использование действий контроллера.</strong> Этот вариант гораздо интереснее. Во-первых, вы автоматически получаете доступ ко всем возможностям Yii. Во-вторых, в этом случае XML-RPC методы можно разделить по модулям. Т.е. подключение / отключение модуля будет автоматически подключать / отключать соответствующие XML-RPC методы.<br />
<span id="more-1247"></span><br />
Т.к. реализация первого подхода никакой сложности не представляет (фактически вы можете использовать примеры из документации к вашей библиотеке для работы с XML-RPC), мы рассмотрим второй вариант.</p>
<h2>Какие библиотеки можно использовать?</h2>
<p>По большому счёту – любые, т.к. подключение сторонних библиотек к Yii выполняется без особых проблем. Я обычно использую библиотеку <a href="http://scripts.incutio.com/xmlrpc/manual.php">Incutio XML-RPC Library</a>. Она достаточно простая, работает без проблем и, кроме того, используется в WordPress, а эта CMS используется очень часто.</p>
<p>Переходим к практике.</p>
<h2>Шаг 1. Подключаем библиотеку.</h2>
<p>Для этого скачиваем библиотеку и сохраняем файл <code>IXR_Library.php</code> в папку <code>protected/vendors</code>. Теперь подключить библиотеку можно так:</p>
<pre class="brush: php">Yii::import('application.vendors.*');
require_once ('IXR_Library.php');</pre>
<p>Этот код можно добавить в любое действие (<code>action</code>) контроллера.</p>
<h2>Шаг 2. Создаём точку входа.</h2>
<p>Как я уже говорил, в качестве точки входа мы используем действие контроллера. Код будет выглядеть примерно так:</p>
<pre class="brush: php">class MyController extends Controller {
	...
	public function actionXmlrpc() {
		Yii::import('application.vendors.*');
		require_once ('IXR_Library.php');

		$server = new IXR_Server(array(
			'my.hello' =&gt; array($this,'hello'),
		));
	}
	...
}</pre>
<p>В этом методе мы просто создаём объект типа <code>IXR_Server</code>, конструктору которого нужно передать массив с названиями XML-RPC методов. В данном случае в массиве только один элемент. Ключ этого элемента является названием XML-RPC метода, а значение – именем функции, которую нужно вызвать.</p>
<p><code>array($this,'hello')</code> означает, что будет вызван метод <code>hello</code> данного контроллера.</p>
<p>Тут нужно помнить об одном нюансе. Обычно в XML-RPC запросах не передаются данные сессий. Логин и пароль отправляются в каждом запросе в качестве параметров. Поэтому необходимо, обеспечить доступ к методу <code>actionXmlrpc()</code> без авторизации. Для этого немного изменим метод <code>accessRules</code></p>
<pre class="brush: php">public function accessRules() {
	return array(
		array('allow',  // allow all users to perform 'index' and 'view' actions
			'actions'=&gt;array('index','view','xmlrpc'),
			'users'=&gt;array('*'),
		),
		…,
	);
}</pre>
<h2>Шаг 3. Добавляем методы.</h2>
<p>Теоретически, можно ограничиться просто добавлением метода <code>hello</code>, который просто вернет «Hello, world!».</p>
<pre class="brush: php">public function hello($args) {
	return 'Hello, world!';
}</pre>
<p>Но в 99% нужно аутентифицировать пользователя. Добавим метод</p>
<pre class="brush: php">protected function xmlrpcLogin($username, $password) {
	$identity = new UserIdentity($username, $password);
	$identity-&gt;authenticate();
	if ($identity-&gt;errorCode === UserIdentity::ERROR_NONE) {
		Yii::app()-&gt;user-&gt;login($identity, 0);
		return true;
	}
	else {
		return false;
	}
}</pre>
<p>Здесь мы используем класс <code>UserIdentity</code>, который автоматически создаётся фреймворком Yii при создании приложения. В параметрах этого метода нужно передать логин и пароль. В случае успешной аутентификации метод вернёт TRUE, в противном случае – FALSE. Кроме того, данные пользователя будут доступны через Yii::app()-&gt;user, т.е. точно также как и при обычной работе с фреймворком.</p>
<p>Теперь перепишем код метода hello.</p>
<pre class="brush: php">public function hello($args) {
	if (!$this-&gt;xmlrpcLogin($args[0], $args[1])) {
		return new IXR_Error(-1, 'You did not provide the correct password');
	}
return 'Hello, world!';
}</pre>
<p>Здесь предполагается, что логин и пароль будут переданы в первом и втором параметрах XML-RPC запроса.</p>
<h2>Шаг 4. Проверяем работу.</h2>
<p>Для проверки достаточно создать обычный PHP скрипт со следующим кодом.</p>
<pre class="brush: php">require_once ('IXR_Library.php');

$client = new IXR_Client('http://my_site.domen/my/xmlrpc');
$client-&gt;debug = true;

$args = array();
$args[0] = 'login';
$args[1] = 'password';

if (!$client-&gt;query('my.hello', $args)) {
    die('Something went wrong - '.$client-&gt;getErrorCode().' : '.$client-&gt;getErrorMessage());
}
echo $client-&gt;getResponse();</pre>
<p>Как видите, создание XML-RPC сервера особой сложности не представляет. Конечно, самая сложная работа – это написание самих методов. Но не забывайте, что если у вас уже готов web интерфейс и логика по максимуму вынесена в модели, то вы сможете спокойно использовать её и в XML-RPC методах.</p>
<p><strong>Успехов!</strong></p>
<p><em>Полезная информация</em></p>
<p>Новогодний подарок от Inferno Solutions &#8211; всем новым клиентам панель ISP бесплатно и 30$ в подарок при заказе <a href="https://cp.inferno.name/cart.php">VPS в Германии</a>, Украине или Голландии! Укажите при заказе в примечании ISP+30.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.simplecoding.org/yii-frejmvork-sozdanie-xml-rpc-servera.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Как «подружить» сервис Loginza и фреймворк Yii</title>
		<link>http://www.simplecoding.org/kak-podruzhit-servis-loginza-i-frejmvork-yii.html</link>
		<comments>http://www.simplecoding.org/kak-podruzhit-servis-loginza-i-frejmvork-yii.html#comments</comments>
		<pubDate>Wed, 29 Sep 2010 07:53:12 +0000</pubDate>
		<dc:creator>Владимир</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web разработка]]></category>
		<category><![CDATA[Yii]]></category>

		<guid isPermaLink="false">http://www.simplecoding.org/?p=1127</guid>
		<description><![CDATA[Постоянные читатели этого блога, наверное, помнят, что и о фреймворке Yii, и о сервисе аутентификации Loginza я раньше рассказывал. Найти эти статьи несложно: Yii, Loginza. Но в статье о Loginza, речь шла о протоколе обмена данными с сервисом, а вопрос аутентификации остался «за бортом». Я, конечно, объяснил когда нужно создавать сессию, но на практике этого [...]]]></description>
			<content:encoded><![CDATA[<div id="attachment_1129" class="wp-caption alignnone" style="width: 310px"><img src="http://www.simplecoding.org/wp-content/uploads/2010/09/loginza_yii.jpg" alt="loginza yii" title="loginza yii" width="300" height="228" class="size-full wp-image-1129" style="float:left" /><p class="wp-caption-text"> </p></div>
<p>Постоянные читатели этого блога, наверное, помнят, что и о <strong>фреймворке Yii</strong>, и о <strong>сервисе аутентификации Loginza</strong> я раньше рассказывал. Найти эти статьи несложно: <a href="http://www.simplecoding.org/category/yii">Yii</a>, <a href="http://www.simplecoding.org/authentication-with-social-services-and-openid.html">Loginza</a>.</p>
<p>Но в статье о Loginza, речь шла о протоколе обмена данными с сервисом, а вопрос аутентификации остался «за бортом». Я, конечно, объяснил когда нужно создавать сессию, но на практике этого явно недостаточно <img src='http://www.simplecoding.org/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p>В этой статье я постараюсь исправить этот недостаток и покажу пример аутентификации пользователя с помощью сервиса Loginza.</p>
<p>Прежде всего, определим требования к такой системе.<br />
<span id="more-1127"></span><br />
1) Она не должна нарушать работу стандартной системы аутентификации (с использованием логина и пароля).</p>
<p>2) Должны поддерживаться стандартные средства Yii для работы с пользователями. Речь о компоненте Yii::app()-&gt;user (CWebUser).</p>
<p>3) Завершение сеанса (<code>Yii::app()-&gt;user-&gt;logout()</code>) должно работать независимо от того каким образом была выполнена аутентификация.</p>
<p>4) Если посетитель впервые зашел на сайт и успешно прошел аутентификацию с помощью Loginza, для него нужно автоматически создать аккаунт.</p>
<p>5) Нужна возможность удобно показывать ссылку «Войти с помощью Loginza» на любой странице сайта (виджет).</p>
<p>6) После аутентификации пользователь должен вернуться на исходную страницу.</p>
<p>Чтобы лучше представить фронт работ, рассмотрим <strong>алгоритм аутентификации фреймворка Yii</strong>.</p>
<p>1. Посетитель кликает по кнопке «Войти с помощью Loginza».</p>
<p>2. Появляется виджет в котором пользователь выбирает сервис аутентификации.</p>
<p>3. Loginza проводит аутентификацию и возвращает посетителя на наш сайт. При этом в массиве <code>$_POST</code> передается <code>token</code>, с помощью которого можно получить результаты аутентификации пользователя.</p>
<p>4. Наше приложение должно.</p>
<p>4.1. Получить данные от сервиса Loginza.</p>
<p>4.2. Создать пользователя (экземпляр класса, реализующий интерфейс <code>IUserIdentity</code>). Этот класс содержит метод <code>authenticate</code> в котором мы проверим, существует ли данный пользователь в БД, если нет – создадим его, и установим данные, которые необходимы для работы класса <code>CWebUser</code>.</p>
<p>4.3.	Выполнить аутентификацию. Т.е. вызвать <code>Yii::app()-&gt;user-&gt;login</code>. При этом в первом параметре метода <code>login</code> мы передаем экземпляр класса, реализующего IUserIdentity (который мы создали на предыдущем этапе). Таким образом, мы обеспечим нормальную работу нашего класса со всеми остальными компонентами фреймворка.</p>
<p>Переходим к реализации.</p>
<p><strong>Шаг 1. Создадим виджет, показывающий ссылку «Войти с помощью Loginza».</strong></p>
<p>Он будет состоять из двух файлов:<br />
1) <code>protected/components/LoginzaWidget.php</code> – класс виджета<br />
2) <code>protected/components/views/loginzaWidget.php</code> – представление.</p>
<p>Рассмотрим класс виджета.</p>
<pre class="brush: php">&lt;?php
class LoginzaWidget extends CWidget {
	//параметры по-умолчанию
	private $params = array(
		'widget_url'=&gt;'https://s3-eu-west-1.amazonaws.com/s1.loginza.ru/js/widget.js',
		'token_url'=&gt;'https://loginza.ru/api/widget?token_url=',
		'return_url'=&gt;'',
		'link_anchor'=&gt;'Войти с помощью Loginza',
		'logout_url'=&gt;'',
		'css_class'=&gt;'loginza',
		'providers_set'=&gt;array('vkontakte', 'facebook', 'twitter', 'loginza'
            , 'myopenid', 'webmoney', 'rambler', 'flickr', 'lastfm', 'openid'
            , 'mailru', 'verisign', 'aol', 'steam', 'google', 'yandex'
            , 'mailruapi'),
	);

	/**
	 * Этот метод подключает JS скрипт и загружает представление
	 */
    public function run() {
        //подключаем JS скрипт
        Yii::app()-&gt;clientScript-&gt;registerScriptFile(
				$this-&gt;params['widget_url']
                , CClientScript::POS_END);
        $this-&gt;render('loginzaWidget', $this-&gt;params);
    }

	/**
	 * Установка параметров
	 * @param array $params массив с параметрами
	 */
	public function setParams($params) {
		$this-&gt;params = array_merge($this-&gt;params, $params);
	}
}</pre>
<p>Здесь мы создаем массив с дефолтными параметрами, в котором указываем настройки, необходимые для работы с сервисом Loginza. Подробно на них останавливаться не будем, т.к. они рассмотрены в <a href="http://www.simplecoding.org/authentication-with-social-services-and-openid.html">предыдущей статье</a>. Но обратите внимание, что обязательно нужно установить два параметра: <code>return_url</code> и <code>logout_url</code>.</p>
<p>В первом (return_url) нужно указать адрес, на который должен быть перенаправлен пользователь после аутентификации на Loginza.</p>
<p>Во втором (logout_url) – адрес метода, выполняющего завершение сессии.</p>
<p>Эти методы выполняют непосредственно аутентификацию пользователя в нашем приложении, и логично, что виджет о них ничего не знает.</p>
<p>Таким образом, подключить виджет можно так.</p>
<pre class="brush: php">&lt;?php  $this-&gt;widget('application.components.LoginzaWidget', array(
	'params'=&gt;array(
		'return_url'=&gt;'site/loginzalogin',
		'logout_url'=&gt;'site/logout',
		'providers_set'=&gt;array('google','vkontakte','facebook','twitter','rambler','openid','mailru','yandex','mailruapi'),
	),
)); ?&gt;</pre>
<p>В представлении (<code>protected/components/views/loginzaWidget.php</code>) мы проверяем, аутентифицирован ли пользователь и в зависимости от результата выводим ссылку «Войти с помощью Loginza» или «Выход».</p>
<pre class="brush: php">&lt;?php if (Yii::app()-&gt;user-&gt;isGuest) {
	$url = $token_url.Yii::app()-&gt;createAbsoluteUrl($return_url
			, array('providers_set'=&gt;implode(',',$providers_set)));
	echo CHtml::link($link_anchor, $url, array('class'=&gt;$css_class));
} else {
	$anchor = 'Выйти ('.Yii::app()-&gt;user-&gt;getName().')';
	echo CHtml::link($anchor, array($logout_url));
}
?&gt;</pre>
<p><strong>Шаг 2. Напишем метод, выполняющий аутентификацию посетителя.</strong></p>
<p>Я добавил этот метод в класс <code>SiteController</code>, который автоматически создается утилитой yiic при создании приложения.</p>
<pre class="brush: php">public function actionLoginzaLogin() {
	//проверяем, пришел ли token
	if (isset($_POST['token'])) {
		$loginza = new LoginzaModel();
		$loginza-&gt;setAttributes($_POST);
		$loginza-&gt;getAuthData();
		if ($loginza-&gt;validate() &#038;&#038; $loginza-&gt;login()) {
			//возвращаем пользователя на ту страницу на которой он
			//находился перед аутентификацией
			$this-&gt;redirect(Yii::app()-&gt;user-&gt;returnUrl);
		}
		else {
			//сообщение об ошибке
			$this-&gt;render('loginzaerror');
		}
	}
	else {
		//если этот метод вызван напрямую (без указания token)
		$this-&gt;redirect(Yii::app()-&gt;homeUrl, true);
	}
}</pre>
<p>Этот метод мы указали в параметре <code>return_url</code> при настройке виджета, таким образом, именно на него придет редирект от сервиса Loginza.</p>
<p>Мы проверяем наличие параметра <code>token</code> в массиве <code>$_POST</code> и если он найден, продолжаем процедуру аутентификации.</p>
<p>От Loginza мы получаем данные о результатах аутентификации, и проверить их будет совсем не лишним. Мы, конечно, можем написать весь код самостоятельно, но гораздо удобнее использовать встроенные средства фреймворка.</p>
<p><strong>Шаг 3. Создаем модель, выполняющую обработку данных от Loginza.</strong></p>
<p>Модель будет называться <code>LoginzaModel</code>, но прежде чем переходить к ее детальному рассмотрению, определимся, какие данные о пользователе нужно хранить в БД.</p>
<p>Loginza передает в обязательно порядке два параметра: <code>identity</code> и <code>provider</code> (именно они позволяют однозначно идентифицировать пользователя), остальные зависят от выбранного сервиса аутентификации.</p>
<p>Кроме того, полезно будет хранить email пользователя и его имя. К сожалению, в зависимости от сервиса вы можете получить эти данные, а можете и не получить <img src='http://www.simplecoding.org/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Теперь рассмотрим сам класс.</p>
<pre class="brush: php">&lt;?php
class LoginzaModel extends CModel {

	public $identity;
	public $provider;
	public $email;
	public $full_name;
	public $token;
	public $error_type;
	public $error_message;

	private $loginzaAuthUrl = 'http://loginza.ru/api/authinfo?token=';

	public function rules() {
		return array(
			array('identity,provider,token', 'required'),
			array('email', 'email'),
			array('identity,provider,email', 'length', 'max'=&gt;255),
			array('full_name', 'length', 'max'=&gt;55),
		);
	}

	public function attributeLabels() {
		return array(
			'identity'=&gt;'Идентификатор сервиса аутентификации',
			'provider'=&gt;'Сервис аутентификации',
			'email'=&gt;'eMail',
			'full_name'=&gt;'Имя',
		);
	}

	/**
	 * Получение данных от сервиса Loginza.
	 * Предварительно нужно установить $this-&gt;token
	 * Например, так
	 * $loginza = new LoginzaModel();
	 * $loginza-&gt;setAttributes($_POST);
	 */
	public function getAuthData() {
        //получаем данные от сервера Loginza
        $authData = json_decode(
				file_get_contents($this-&gt;loginzaAuthUrl.$this-&gt;token)
				,true);

		//устанавливаем атрибуты
		//если будут отсутствовать identity и provider, метод validate
		//выдаст ошибку
		$this-&gt;setAttributes($authData);
		//full_name находится внутри вложенного массива
		//TODO доделать установку имени для разных сервисов
		$this-&gt;full_name = (isset($authData['name']['full_name'])) ? $authData['name']['full_name'] : $authData['identity'];
	}

	/**
	 * Аутентификация посетителя.
	 * @return boolean true - если посетитель аутентифицирован, false - в противном случае.
	 */
	public function login() {
		$identity = new LoginzaUserIdentity();
		if ($identity-&gt;authenticate($this)) {
			$duration = 3600*24*30; // 30 days
			Yii::app()-&gt;user-&gt;login($identity,$duration);
			return true;
		}
		return false;
	}

	public function attributeNames() {
		return array(
			'identity'
			,'provider'
			,'email'
			,'full_name'
			,'token'
			,'error_type'
			,'error_message'
		);
	}
}</pre>
<p>Обратите внимание, что мы наследуем его от <code>CModel</code> и, таким образом, получаем возможность использовать встроенные средства проверки данных. Т.е. мы просто объявляем метод <code>rules</code>, который возвращает массив с правилами проверки.</p>
<p>Метод <code>getAuthData</code> отправляет запрос серверу Loginza, обрабатывает результат и устанавливает значения атрибутам этого класса.</p>
<p>Вы, наверное, заметили, что установка имени пользователя немного не доделана. Дело в том, что сервисы передают эти данные в разных элементах массива. Элемент <code>full_name</code> устанавливает Google. По большому счету, нужно просмотреть данные, которые возвращают остальные сервисы и соответственно устанавливать <code>$this-&gt;full_name</code>.</p>
<p>Метод <code>login</code> в случае успешной аутентификации создает сессию для пользователя. В нём мы создаем экземпляр класса <code>LoginzaUserIdentity</code>, который реализует интерфейс <code>IUserIdentity</code>, и передаем его в первом параметре <code>Yii::app()-&gt;user-&gt;login</code>.</p>
<p>Также <code>LoginzaUserIdentity</code> содержит метод <code>authenticate</code>, который выполняет поиск пользователя в БД и, в случае необходимости, создает новый аккаунт.</p>
<p>В зависимости от результата, метод <code>login</code> возвращает true или false.</p>
<p>Прежде чем переходить к рассмотрению <code>LoginzaUserIdentity</code>, создадим таблицу в БД в которой будем хранить данные пользователей и классы, необходимые, для работы с ней.</p>
<p><strong>Шаг 4. Создаем таблицу User</strong></p>
<p>Тут все просто.</p>
<p>Для создания таблицы можно использовать следующий SQL запрос</p>
<pre class="brush: sql">CREATE  TABLE IF NOT EXISTS `tablename`.`user` (
  `u_id` INT NOT NULL AUTO_INCREMENT ,
  `u_identity` VARCHAR(255) NOT NULL ,
  `u_provider` VARCHAR(255) NOT NULL ,
  `u_email` VARCHAR(255) NULL ,
  `u_full_name` VARCHAR(55) NULL ,
  `u_state` TINYINT(1) NOT NULL ,
  PRIMARY KEY (`u_id`) )
ENGINE = InnoDB;</pre>
<p>А классы, необходимые для выполнения CRUD, можно создать с помощью компонента <code>gii</code>.</p>
<p>Вызывается так.<br />
<code>sitename.domen/index.php?r=gii</code></p>
<p>Предварительно нужно активировать его в настройках приложения (config/main.php)</p>
<pre class="brush: php">'modules'=&gt;array(
	'gii'=&gt;array(
		'class'=&gt;'system.gii.GiiModule',
		'password'=&gt;'yourpass',
	),
),</pre>
<p><strong>Шаг 5. Создаем класс LoginzaUserIdentity.</strong></p>
<p>Размещаться он будет в protected/components.</p>
<p>Как я уже упоминал, он реализует интерфейс <code>IUserIdentity</code> и, таким образом, фреймворк сможет с ним работать.</p>
<pre class="brush: php">&lt;?php
/**
 * Этот класс выполняет аутентификацию и, при необходимости, регистрацию посетителя.
 */
class LoginzaUserIdentity implements IUserIdentity {

	private $id;
	private $name;
	private $isAuthenticated = false;
	private $states = array();

	public function __construct() {
	}

	/**
	 * Аутентификация пользователя.
	 * Этот метод ищет пользователя в БД. Если он не найден, создает нового.
	 * Устанавливает значения атрибутов.
	 *
	 * @param LoginzaModel $loginzaModel модель, содержащая данные от сервиса Loginza
	 * @return boolean true если пользователь найден или создан новый аккаунт, false - если недостаточно данных
	 */
	public function authenticate($loginzaModel = null) {

		if (empty($loginzaModel-&gt;identity) || empty($loginzaModel-&gt;provider)) {
			return false;
		}
        //сначала проверяем, существует ли такой пользователь в БД
        $criteria=new CDbCriteria;
        $criteria-&gt;condition = 'u_identity=:identity AND u_provider=:provider';
        $criteria-&gt;params = array(
            ':identity'=&gt;$loginzaModel-&gt;identity
            , ':provider'=&gt;$loginzaModel-&gt;provider
        );
		$user = User::model()-&gt;find($criteria);
		if (null !== $user) {
			//используем существующего пользователя
			$this-&gt;id = $user-&gt;u_id;
			$this-&gt;name = (null != $user-&gt;u_full_name) ? $user-&gt;u_full_name : $user-&gt;u_identity;
		}
		else {
			//создаем нового
			$user = new User();
			$user-&gt;u_identity = $loginzaModel-&gt;identity;
			$user-&gt;u_provider = $loginzaModel-&gt;provider;
			$user-&gt;u_email = $loginzaModel-&gt;email;
			$user-&gt;u_full_name = $loginzaModel-&gt;full_name;
			$user-&gt;save();
			$this-&gt;id = $user-&gt;u_id;
			$this-&gt;name = (null != $user-&gt;u_full_name) ? $user-&gt;u_full_name : $user-&gt;u_identity;
		}
		$this-&gt;isAuthenticated = true;
		return true;
	}

	public function getId() {
		return $this-&gt;id;
	}

	public function getIsAuthenticated() {
		return $this-&gt;isAuthenticated;
	}

	public function getName() {
		return $this-&gt;name;
	}

	public function getPersistentStates() {
		return $this-&gt;states;
	}
}</pre>
<p>Основную часть работы выполняет метод authenticate. Рассмотрим его подробнее.</p>
<p>Прежде всего, мы проверяем, есть ли у нас необходимые данные для поиска пользователя или создания нового аккаунта. Это атрибуты <code>identity</code> и <code>provider</code> модели.</p>
<p>Затем выполняем поиск пользователя в БД. Модель User у нас создана, поэтому поиск можно выполнить с помощью метода find.<br />
<code>User::model()-&gt;find($criteria)</code><br />
Здесь <code>$criteria</code> – экземпляр <code>CDbCriteria</code> в котором мы установили условие &#039;u_identity=:identity AND u_provider=:provider&#039;.</p>
<p>Если пользователь найден, устанавливаем значения атрибутов name, <code>id</code> и <code>isAuthenticated</code>. Если нет &#8211; создаем нового.</p>
<p>Остальные методы необходимо реализовать, т.к. они объявлены в интерфейсе <code>IUserIdentity</code>. Они возвращают основные данные о текущем пользователе.</p>
<p>Благодаря им обеспечивается совместная работа с классом <a href="http://www.yiiframework.com/doc/api/CWebUser">CWebUser</a>. Например, будут правильно установлены атрибуты<br />
<code>Yii::app()-&gt;user-&gt;isGuest</code>, <code>Yii::app()-&gt;user-&gt;name</code> и т.п.</p>
<p><strong>Шаг 6. Обеспечиваем возврат пользователя на исходную страницу.</strong></p>
<p>Т.к. в процессе аутентификации выполняется несколько редиректов, а первый этап выполняется с помощью JS кода, то нам нужно заранее сохранить адрес страницы, на которой находился пользователь. Сделать это проще всего с помощью сессий.</p>
<p>К тому же, есть довольно удобный компонент под названием <a href="http://code.google.com/p/yiiext/downloads/detail?name=setReturnUrl_1.0.2.zip&#038;can=2&#038;q=">setReturnUrl</a>.</p>
<p>Он устанавливается как фильтр</p>
<pre class="brush: php">public function filters() {
	return array(
		array(
			'ESetReturnUrlFilter - login, loginzalogin, logout',
		),
	);
}</pre>
<p>Здесь мы указываем, что этот фильтр не должен применяться к методам <code>login</code>, <code>loginzalogin</code>, <code>logout</code>, т.к. мы должны вернуть пользователя на страницу, на которой он находился до того, как они были вызваны.</p>
<p>На этом я буду завершать статью. Возможно, некоторые моменты, касающиеся настройки компонентов, я упустил, поэтому, если возникнут затруднения, качайте архив с исходниками этого примера.</p>
<p><a href='http://www.simplecoding.org/wp-content/uploads/2010/09/yii_loginza.zip'><img src="http://www.simplecoding.org/wp-content/themes/three_cols/images/download_btn_blue.png" alt="архив с исходным кодом" /></a></p>
<p>Рассмотреть каждый мелкий нюанс практически невозможно, статья бы получилась просто огромной <img src='http://www.simplecoding.org/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' />  . К тому же, я предполагаю, что у вас есть хотя бы общее представление о фреймворке Yii.</p>
<p>Но, в любом случае, я будут рад ответить на вопросы и обсудить замечания и предложения!</p>
<p>До встречи!</p>
<p><strong>Интересно почитать</strong></p>
<p>Четыре способа <a href="http://blog.copy-write.ru/contact/sposoby-i-metody-uvelicheniya-obemov-prodazh">увеличения продаж</a> на конкурентном б2б-рынке</p>
]]></content:encoded>
			<wfw:commentRss>http://www.simplecoding.org/kak-podruzhit-servis-loginza-i-frejmvork-yii.html/feed</wfw:commentRss>
		<slash:comments>39</slash:comments>
		</item>
		<item>
		<title>Yii фреймворк: установка дефолтных параметров для виджетов</title>
		<link>http://www.simplecoding.org/yii-frejmvork-ustanovka-defoltnyx-parametrov-dlya-vidzhetov.html</link>
		<comments>http://www.simplecoding.org/yii-frejmvork-ustanovka-defoltnyx-parametrov-dlya-vidzhetov.html#comments</comments>
		<pubDate>Mon, 27 Sep 2010 07:12:03 +0000</pubDate>
		<dc:creator>Владимир</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web разработка]]></category>
		<category><![CDATA[Yii]]></category>

		<guid isPermaLink="false">http://www.simplecoding.org/?p=1124</guid>
		<description><![CDATA[Сегодня хочу показать небольшую особенность, которую нужно учитывать при разработке виджетов для фреймворка Yii. В большинстве случаев, виджеты состоят из файла самого виджета и файла с представлением. Например, у нас есть файлы. protected/components/MyWidget.php – класс виджета и protected/components/views/myWidget.php – представление. Класс виджета может выглядеть примерно так. class MyWidget extends CWidget { public function run() { [...]]]></description>
			<content:encoded><![CDATA[<div id="attachment_1125" class="wp-caption alignnone" style="width: 300px"><img src="http://www.simplecoding.org/wp-content/uploads/2010/09/yii_widget_parameters.png" alt="yii widget parameters" title="yii widget parameters" width="290" height="158" class="size-full wp-image-1125" style="float:left" /><p class="wp-caption-text"> </p></div>
<p>Сегодня хочу показать небольшую особенность, которую нужно учитывать при разработке <strong>виджетов для фреймворка <a href="http://www.yiiframework.com/">Yii</a></strong>.</p>
<p>В большинстве случаев, <a href="http://yiiframework.ru/doc/guide/ru/basics.view">виджеты</a> состоят из файла самого виджета и файла с представлением.</p>
<p>Например, у нас есть файлы.<br />
<code>protected/components/MyWidget.php</code> – класс виджета<br />
и<br />
<code>protected/components/views/myWidget.php</code> – представление.</p>
<p>Класс виджета может выглядеть примерно так.<br />
<span id="more-1124"></span></p>
<pre class="brush: php">class MyWidget extends CWidget {

	public function run() {
		$this-&gt;render('myWidget');
	}
}</pre>
<p>А в представлении можно записать что-то вроде</p>
<pre class="brush: html">&lt;h1&gt;Привет!&lt;/h1&gt;</pre>
<p>В таком варианте все будет прекрасно работать, но пользы от такого виджета немного.</p>
<p>Часто в представлении нужно показать какие-нибудь данные. Они могут быть получены откуда угодно: из модели, из конфигурационной файла, из параметров запроса и т.д.</p>
<p>При этом, передавать их в представление удобнее всего если они сохранены в виде <strong>ассоциативного массива</strong>, т.к. метод <code>render</code> принимает во втором параметре именно массив.</p>
<p>Теперь представьте такую ситуацию. Мы создаем виджет, для работы которого нужна <strong>группа параметров</strong>. При этом, возможно, <strong>часть из них</strong> нужно будет изменять для настройки виджета под конкретный проект.</p>
<p>Т.е. код выглядит приблизительно так</p>
<pre class="brush: php">class MyWidget extends CWidget {
	public $params = array(
		'parameter 1'=&gt;'value 1',
		'parameter 2'=&gt;'value 2',
		'parameter 3'=&gt;'value 3',
	);

	public function run() {
		$this-&gt;render('myWidget', $this-&gt;params);
	}
}</pre>
<p>Теперь взгляните на код вставки виджета на страницу.</p>
<pre class="brush: php">$this-&gt;widget('application.components.MyWidget');</pre>
<p>Тут все отлично, но что если мы захотим изменить &#039;<code>parameter 3</code>&#039;? Во втором параметре метода widget мы можем передать наши значения.</p>
<pre class="brush: php">$this-&gt;widget('application.components.MyWidget', array(
	'params'=&gt;array(
		'parameter 3'=&gt;'new value 3',
	);
));</pre>
<p>Но такой вызов перезапишет весь массив <code>$params</code>, т.е. <code>'parameter 1'</code> и <code>'parameter 2'</code> будут стерты.</p>
<p>Можно, конечно, передать весь массив, но, во-первых, элементов может быть много, а, во-вторых, нет никакого смысла задавать по нескольку раз одни и те же значения.</p>
<p>Лучше решить проблему так.</p>
<p><strong>1) Делаем массив $params приватным</strong>.</p>
<pre class="brush: php">private $params = array(
...
);</pre>
<p><strong>2) Добавляем метод setParams</strong> в класс виджета (название образуется с из названия атрибута класса, значение которого нужно установить (в данном случае <code>Params</code>), и приставки <code>set</code>). Этот метод в первом параметре получит новое значение атрибута. В этом примере – массив с измененным параметром. Нам остается только объединить массивы.</p>
<pre class="brush: php">public function setParams($params) {
	$this-&gt;params = array_merge($this-&gt;params, $params);
}</pre>
<p>Этот же прием можно использовать для проверки параметров или их предварительной обработки.</p>
<p>Если есть замечания или вопросы, пишите, постараюсь ответить <img src='http://www.simplecoding.org/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p>Удачи!</p>
<p><strong>Интересно почитать</strong></p>
<p>Предлагаем <a href="http://onyx-inter.ru/proizvodstvo_avosek">авоськи оптом</a> из любых органических тканей</p>
]]></content:encoded>
			<wfw:commentRss>http://www.simplecoding.org/yii-frejmvork-ustanovka-defoltnyx-parametrov-dlya-vidzhetov.html/feed</wfw:commentRss>
		<slash:comments>21</slash:comments>
		</item>
		<item>
		<title>Yii PHP framework: контроль доступа с использованием ролей (RBAC)</title>
		<link>http://www.simplecoding.org/yii-php-framework-kontrol-dostupa-s-ispolzovaniem-rolej-rbac.html</link>
		<comments>http://www.simplecoding.org/yii-php-framework-kontrol-dostupa-s-ispolzovaniem-rolej-rbac.html#comments</comments>
		<pubDate>Fri, 06 Aug 2010 17:50:38 +0000</pubDate>
		<dc:creator>Владимир</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web разработка]]></category>
		<category><![CDATA[Yii]]></category>

		<guid isPermaLink="false">http://www.simplecoding.org/?p=1109</guid>
		<description><![CDATA[Я думаю, все, кто хоть немного работал с фреймворком Yii знают, что он поддерживает возможность разграничения прав доступа на основе ролей. Принцип работы этой системы достаточно прост. Вы создаёте наборы правил и пользователей, связываете их между собой. После этого, вы в любой момент можете проверить, имеет ли пользователь право на выполнение какой-то операции или нет. [...]]]></description>
			<content:encoded><![CDATA[<div id="attachment_1110" class="wp-caption alignnone" style="width: 310px"><img src="http://www.simplecoding.org/wp-content/uploads/2010/08/yii_rbac.png" alt="yii rbac" title="yii rbac" width="300" height="163" class="size-full wp-image-1110" style="float:left" /><p class="wp-caption-text"> </p></div>
<p>Я думаю, все, кто хоть немного работал с фреймворком <strong>Yii</strong> знают, что он поддерживает возможность разграничения прав доступа на основе ролей.</p>
<p><strong>Принцип работы этой системы</strong> достаточно прост. Вы создаёте наборы правил и пользователей, связываете их между собой. После этого, вы в любой момент можете проверить, имеет ли пользователь право на выполнение какой-то операции или нет.</p>
<p>Одно из основных преимуществ использования данной библиотеки заключается в том, что вам нужно написать минимум кода для проверки прав доступа. Обычно этот код выглядит следующим образом.</p>
<pre class="brush: php">if (!Yii::app()-&gt;user-&gt;checkAccess('createUser')) {
	throw new CHttpException(403, 'Forbidden');
}
//остальной код…</pre>
<p>В теории всё просто. Но на практике, документации и примеров по этой теме практически нет (надеюсь, это скоро изменится).</p>
<p>Основные источники информации (на русском): <a href="http://yiiframework.ru/doc/guide/ru/topics.auth">Аутентификация и авторизация</a> и <a href="http://yiiframework.ru/doc/cookbook/ru/access.rbac.file">RBAC и описание ролей в файле</a>. На английском хороших и подробных примеров, к сожалению, я не нашел.</p>
<p><em>Примечание</em>. Очень советую прочитать эти статьи, прежде чем переходить к моему примеру.</p>
<p>Когда я первый раз решил использовать <strong>RBAC</strong>, то выяснилось, что есть множество нюансов, которые приходится учитывать при работе с этой библиотекой. Ничего запредельно сложного и недоступного для понимания, но «ковырялся» я довольно долго <img src='http://www.simplecoding.org/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /><br />
<span id="more-1109"></span><br />
В этой статье мы рассмотрим пример создания несложной системы управления пользователями (идея взята из одного web приложения).</p>
<p><strong>Постановка задачи.</strong></p>
<p>Есть три типа пользователей.</p>
<p>Обычный (<code>user</code>) – может создавать и редактировать свои данные (например, список контактов) и изменять свои логин/пароль.</p>
<p>Администратор (<code>admin</code>) – может создавать новых пользователей, но не может изменять их данные. Он может изменить свои собственные логин и пароль, но не может изменить роль.</p>
<p>Суперпользователь (<code>root</code>) – может выполнять любые операции с пользователями.</p>
<p>Ни <code>admin</code>, ни <code>root</code> не имеют доступа к персональным данным пользователя (которые он создаёт при работе с приложением).</p>
<p><em>Примечание</em>. Чтобы сократить объем кода, я урезал набор правил. Но, думаю, вы без особого труда сможете его дополнить. Главное понять идею и принцип работы.</p>
<p><strong>Шаг первый. Выбираем тип хранилища для ролей и операций.</strong></p>
<p>Для этих целей Yii позволяет использовать PHP файл (<code>CPhpAuthManager</code>) или базу данных (<code>CDbAuthManager</code>).</p>
<p>Если вы предполагаете, что количество пользователей будет большим, то лучше использовать БД. Но для знакомства с библиотекой лучше использовать PHP файл, т.к. читать его легче. К тому же перейти от одного типа хранилища к другому совсем несложно.</p>
<p>Для того, чтобы Yii «узнал» о вашем выборе, в массив с настройками (config/main.php) нужно добавить следующий элемент.</p>
<pre class="brush: php">'components'=&gt;array(
…
	'authManager'=&gt;array(
		'class' =&gt; 'CPhpAuthManager',
	),
),</pre>
<p><strong>Шаг второй. Создадим таблицу в БД для хранения пользователей.</strong></p>
<p>Эта таблица будет состоять из шести полей.</p>
<p><code>u_id</code> – первичный ключ;<br />
<code>u_name</code> – имя пользователя;<br />
<code>u_email</code> – адрес почты (используется как логин);<br />
<code>u_pass</code> – пароль;<br />
<code>u_state</code> – статус (активен, заблокирован);<br />
<code>u_role</code> – роль пользователя (<code>root</code>, <code>admin</code>, <code>user</code>).</p>
<p>В данном примере для нас играет роль последнее поле (<code>u_role</code>).</p>
<p><strong>Шаг третий. Создаём операции, роли и задачи.</strong></p>
<p>Я очень надеюсь, что вы прочли статью из <a href="http://yiiframework.ru/doc/guide/ru/topics.auth">Полного руководства</a> и знаете, что представляют собой операции, роли и задачи и зачем они нужны.</p>
<p>Сразу перейдём к созданию файла с этими настройками.</p>
<p>Т.к. в качестве хранилища мы выбрали PHP файл, то можно создать его вручную (этот способ подробно рассмотрен в статье <a href="http://yiiframework.ru/doc/cookbook/ru/access.rbac.file">RBAC и описание ролей в файле</a>).</p>
<p>Но, на мой взгляд, гораздо удобнее использовать API. Поэтому создадим небольшой инсталляционный скрипт (контроллер <code>SiteController</code> метод <code>actionInstall</code>).</p>
<p>Примечание. Данные будут сохранены в файле protected/data/auth.php. Файл будет создан автоматически, поэтому запись в эту папку должна быть разрешена.</p>
<pre class="brush: php">public function actionInstall() {

	$auth=Yii::app()-&gt;authManager;

	//сбрасываем все существующие правила
	$auth-&gt;clearAll();

	//Операции управления пользователями.
	$auth-&gt;createOperation('createUser', 'создание пользователя');
	$auth-&gt;createOperation('viewUsers', 'просмотр списка пользователей');
	$auth-&gt;createOperation('readUser', 'просмотр данных пользователя');
	$auth-&gt;createOperation('updateUser', 'изменение данных пользователя');
	$auth-&gt;createOperation('deleteUser', 'удаление пользователя');
	$auth-&gt;createOperation('changeRole', 'изменение роли пользователя');

	$bizRule='return Yii::app()-&gt;user-&gt;id==$params["user"]-&gt;u_id;';
	$task = $auth-&gt;createTask('updateOwnData', 'изменение своих данных', $bizRule);
	$task-&gt;addChild('updateUser');

	//создаем роль для пользователя admin и указываем, какие операции он может выполнять
	$role = $auth-&gt;createRole('admin');
	$role-&gt;addChild('createUser');
	$role-&gt;addChild('viewUsers');
	$role-&gt;addChild('readUser');
	$role-&gt;addChild('updateOwnData');

	//все пользователи будут создаваться по-умолчанию с ролью user,
	//только root может менять роль другого пользователя

	//создаем роль для пользователя root
	$role = $auth-&gt;createRole('root');
	//наследуем операции, определённые для admin'а и добавляем новые
	$role-&gt;addChild('admin');
	$role-&gt;addChild('updateUser');
	$role-&gt;addChild('deleteUser');
	$role-&gt;addChild('changeRole');

	//создаем операции для user'а
	$bizRule='return Yii::app()-&gt;user-&gt;id==$params["contact"]-&gt;c_user_id;';

	$auth-&gt;createOperation('createContact','создание контакта');
	$auth-&gt;createOperation('viewContacts','просмотр списка контактов');
	$auth-&gt;createOperation('readContact','просмотр контакта', $bizRule);
	$auth-&gt;createOperation('updateContact','редактирование контакта',$bizRule);
	$auth-&gt;createTask('deleteContact','удаление контакта',$bizRule);

	//создаем роль user и добавляем операции для неё
	$user = $auth-&gt;createRole('user');

	$user-&gt;addChild('createContact');
	$user-&gt;addChild('viewContacts');
	$user-&gt;addChild('readContact');
	$user-&gt;addChild('updateContact');
	$user-&gt;addChild('deleteContact');
	$user-&gt;addChild('updateOwnData');

	//создаем пользователя root (запись в БД в таблице users)
	//тут используем DAO, т.к. AR автоматически назначит пользователю роль user
	$sql = 'INSERT INTO users(u_name, u_email, u_pass, u_state, u_role)'
		.' VALUES ("root", "test@test.ru", "'.md5('11111')
		.'", '.Users::STATE_ACTIVE.', "'.Users::ROLE_ROOT.'")';
	$conn = Yii::app()-&gt;db;
	$conn-&gt;createCommand($sql)-&gt;execute();

	//связываем пользователя с ролью
	$auth-&gt;assign('root', $conn-&gt;getLastInsertID());

	//сохраняем роли и операции
	$auth-&gt;save();

	$this-&gt;render('install');
}</pre>
<p>Метод получился довольно объемный, но в нём большую часть занимаю вызовы <code>createOperatio</code>n и <code>addChild</code>, которые создают операции и связывают их с ролями.</p>
<p>Большинство операций в этом примере соответствуют методам контроллера (CRUD), но они могут быть любыми. Например, такими как <code>changeRole</code>, позволяющими изменять одно единственное поле записи.</p>
<p><strong>Обратите внимание</strong>. После создания операций и ролей доступ ограничен не будет. Вы должны будете сами проверить у пользователя наличие прав с помощью метода <code>checkAccess</code>.</p>
<p>Отдельного внимания заслуживает использование бизнес правил (<code>bizRule</code>) в операциях.</p>
<p>Бизнес правило представляет собой обычный PHP код, который должен возвращать <code>true</code> или <code>false</code>. Этот код может получить массив с данными, который будет доступен через переменную <code>$params</code>.</p>
<p>Рассмотрим правило</p>
<pre class="brush: php">$bizRule='return Yii::app()-&gt;user-&gt;id==$params["user"]-&gt;u_id;';</pre>
<p>Здесь мы проверяем, соответствуют ли <code>id</code> текущего пользователя (который выполнил вход) и <code>id</code> записи в таблице пользователей, которую он хочет изменить. Таким образом, это правило позволит пользователю редактировать только свои данные.</p>
<p>В конце метода мы создаём пользователя <code>root</code>. Дело в том, что все пользователи будут создаваться с ролью <code>user</code> и только <code>root</code> может изменять роль. Поэтому мы создаём его сразу при инсталляции.</p>
<p>После создания пользователя назначаем ему роль (метод <code>assign</code>) и сохраняем изменения<br />
<code>$auth-&gt;save();</code></p>
<p><strong>Шаг четвертый. Создание модели для работы с пользователями.</strong></p>
<p>Как обычно, создаём модель с помощью генератора (gii) и затем вносим свои изменения.</p>
<p>Весь код модели я приводить здесь не буду (в конце статьи есть ссылка на архив с примером). Рассмотрим только измененные методы.</p>
<pre class="brush: php">public function beforeSave() {
	parent::beforeSave();
	$this-&gt;u_pass = md5($this-&gt;u_pass);
	/*
	 * Если пользователь не имеет права изменять роль, то мы должны
	 * установить роль по-умолчанию (user)
	 */
	if (!Yii::app()-&gt;user-&gt;checkAccess('changeRole')) {
		if ($this-&gt;isNewRecord) {
			//ставим роль по-умолчанию user
			$this-&gt;u_role = Users::ROLE_USER;
		}
	}
	return true;
}</pre>
<p>Перед созданием новой записи мы проверяем, имеет ли текущей пользователь право изменять роли, если нет, то ставим роль по-умолчанию (<code>user</code>).</p>
<p>После сохранения (или создания) записи, нужно назначить пользователю роль. Права пользователя мы уже проверили и роль установили, поэтому сейчас просто назначаем пользователю роль (метод <code>assign</code>).</p>
<p>Предварительно, с помощью метода <code>revoke</code> удаляем связь между пользователем и ролью (если такая существовала). Если связь не удалить, то когда <code>root</code> будет изменять роли, у нас появятся пользователи с несколькими ролями.</p>
<pre class="brush: php">public function afterSave() {
	parent::afterSave();
	//связываем нового пользователя с ролью
	$auth=Yii::app()-&gt;authManager;
	//предварительно удаляем старую связь
	$auth-&gt;revoke($this-&gt;prevRole, $this-&gt;u_id);
	$auth-&gt;assign($this-&gt;u_role, $this-&gt;u_id);
	$auth-&gt;save();
	return true;
}</pre>
<p>При удалении пользователя не забываем удалить связь между ним и ролью.</p>
<pre class="brush: php">public function beforeDelete() {
	parent::beforeDelete();
	//убираем связь удаленного пользователя с ролью
	$auth=Yii::app()-&gt;authManager;
	$auth-&gt;revoke($this-&gt;u_role, $this-&gt;u_id);
	$auth-&gt;save();
	return true;
}</pre>
<p>Как видите, принцип работы достаточно простой. Главное, не забывайте вызывать <code>$auth-&gt;save();</code> чтобы сохранить изменения.</p>
<p><strong>Шаг пятый. Контроллер и представления.</strong></p>
<p>Как и в случае с моделью, создаем контроллер и представления с помощью генератора (gii).</p>
<p>Методы <code>filters</code> и <code>accessRules</code> можно убрать, т.к. их мы не используем. В остальные методы добавляем проверку прав пользователя.</p>
<p>Опять же, весь контроллер целиком рассматривать нет смысла, он есть в архиве с исходниками. В качестве примера рассмотрим метод обновления данных пользователя.</p>
<p>Это самый сложный случай. У нас есть пользователь, которому можно изменять любые записи, есть пользователи, которым можно изменять только свою запись, но при этом нельзя изменять свою роль. Поэтому проверок будет две.</p>
<p>В первый раз проверяем, пытается ли изменить роль пользователь, у которого на это нет прав.</p>
<p>С помощью второй проверки убеждаемся, что у пользователя есть права на изменение данной записи.</p>
<pre class="brush: php">public function actionUpdate()
{
	$model=$this-&gt;loadModel();

	//проверяем, можно ли пользователю изменять роль
	if (isset($_POST['Users']['u_role']) &#038;&#038; !Yii::app()-&gt;user-&gt;checkAccess('changeRole')) {
		throw new CHttpException(403,'Forbidden');
	}

	//проверяем, может ли пользователь изменять данную запись
	if (!Yii::app()-&gt;user-&gt;checkAccess('updateUser')
			&#038;&#038; !Yii::app()-&gt;user-&gt;checkAccess('updateOwnData', array('user'=&gt;$model))) {
		throw new CHttpException(403,'Forbidden');
	}

	if(isset($_POST['Users']))
	{
		$model-&gt;prevRole = $model-&gt;u_role;
		$model-&gt;attributes=$_POST['Users'];
		if($model-&gt;save())
			$this-&gt;redirect(array('view','id'=&gt;$model-&gt;u_id));
	}

	$this-&gt;render('update',array(
		'model'=&gt;$model,
	));
}</pre>
<p>В представлении (<code>views/users/_form.php</code>) убираем из формы поле «Роль» для пользователя у которого нет прав её изменять.</p>
<pre class="brush: php">…
&lt;?php if (Yii::app()-&gt;user-&gt;checkAccess('changeRole')) { ?&gt;
	&lt;div class="row"&gt;
		&lt;?php echo $form-&gt;labelEx($model,'u_role'); ?&gt;
		&lt;?php echo $form-&gt;dropDownList($model,'u_role',array(
				Users::ROLE_USER=&gt;Users::ROLE_USER,
				Users::ROLE_ADMIN=&gt;Users::ROLE_ADMIN,
				Users::ROLE_ROOT=&gt;Users::ROLE_ROOT,
			));
		?&gt;
		&lt;?php echo $form-&gt;error($model,'u_role'); ?&gt;
	&lt;/div&gt;
&lt;?php } ?&gt;
…</pre>
<p>На этом мы остановимся. Общую идею, надеюсь, я объяснил. Если есть желание, можете скачать архив и поиграться с этим примером.</p>
<p><a href='http://www.simplecoding.org/wp-content/uploads/2010/08/yii_rbac.zip'><img src="http://www.simplecoding.org/wp-content/themes/three_cols/images/download_btn_blue.png" alt="архив с исходным кодом" /></a></p>
<p>В архиве есть дамп базы и папка <code>protected</code> приложения. Само приложение вам нужно будет создать самостоятельно. Дальше, думаю, вы разберетесь <img src='http://www.simplecoding.org/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p>Любые вопросы или замечания оставляйте в комментариях. Постараюсь ответить!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.simplecoding.org/yii-php-framework-kontrol-dostupa-s-ispolzovaniem-rolej-rbac.html/feed</wfw:commentRss>
		<slash:comments>28</slash:comments>
		</item>
		<item>
		<title>CGridView. Часть вторая. AJAX.</title>
		<link>http://www.simplecoding.org/cgridview-chast-vtoraya-ajax.html</link>
		<comments>http://www.simplecoding.org/cgridview-chast-vtoraya-ajax.html#comments</comments>
		<pubDate>Sun, 25 Jul 2010 13:16:41 +0000</pubDate>
		<dc:creator>Владимир</dc:creator>
				<category><![CDATA[Ajax]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web разработка]]></category>
		<category><![CDATA[Yii]]></category>

		<guid isPermaLink="false">http://www.simplecoding.org/?p=1100</guid>
		<description><![CDATA[В этой части я хочу рассказать о некоторых особенностях реализации AJAX запросов в компоненте CGridView. Предположим, у нас есть таблица, и мы создали для неё модель и скрипты для выполнения CRUD операций (с помощью встроенного генератора Yii). Пусть таблица называется countries, содержит список стран с двумя полями (id, name). В этом случае, страница управления записями [...]]]></description>
			<content:encoded><![CDATA[<div id="attachment_1101" class="wp-caption alignnone" style="width: 310px"><img src="http://www.simplecoding.org/wp-content/uploads/2010/07/yii_grid_view1.png" alt="yii grid view" title="yii grid view" width="300" height="169" style="float:left" class="size-full wp-image-1101" /><p class="wp-caption-text"> </p></div>
<p>В этой части я хочу рассказать о некоторых особенностях реализации AJAX запросов в компоненте <a href="http://www.yiiframework.com/doc/api/CGridView">CGridView</a>.</p>
<p>Предположим, у нас есть таблица, и мы создали для неё модель и скрипты для выполнения CRUD операций (с помощью встроенного генератора <strong>Yii</strong>).</p>
<p>Пусть таблица называется <code>countries</code>, содержит список стран с двумя полями (<code>id</code>, <code>name</code>).</p>
<p>В этом случае, страница управления записями будет доступна адресу<br />
<span id="more-1100"></span><br />
<code>index.php?r=countries/admin</code></p>
<div id="attachment_1107" class="wp-caption alignnone" style="width: 460px"><img src="http://www.simplecoding.org/wp-content/uploads/2010/07/cgridview_ajax_1.png" alt="cgridview ajax" title="cgridview ajax" width="450" height="249" class="size-full wp-image-1107" /><p class="wp-caption-text"> </p></div>
<p>В нижней части страницы находится <strong>пагинатор</strong> (листалка). Клик по ссылке с номером страницы приведет к отправке <strong>ajax запроса</strong>. Но вот тут появляется одна из особенностей. Этот ajax запрос возвращает не только новую страницу с данными, но и всю страницу целиком. Начиная от <code>doctype</code> и заканчивая закрывающим тегом <code>html</code>.</p>
<p>Если на странице ничего, кроме таблицы нет, то это не проблема. Но если мы разместим на ней несколько виджетов, для формирования которых нам потребуется выполнить дополнительные запросы к БД, то смысла в такой ajax пагинации окажется немного.</p>
<p>В общем, <strong>нужно, чтобы при клике на номер страницы сервер отправлял только фрагмент разметки с таблицей</strong>.</p>
<p>Сделать это несложно. Для начала вынесем из представления <code>views/countries/admin.php</code> код, который выполняет формирование таблицы в отдельный файл &#8211; <code>views/countries/admingrid.php</code>.</p>
<p>Речь идет об этом фрагменте</p>
<pre class="brush: php">&lt;?php $this-&gt;widget('zii.widgets.grid.CGridView', array(
	'id'=&gt;'countries-grid',
	'dataProvider'=&gt;$model-&gt;search(),
	'filter'=&gt;$model,
	'columns'=&gt;array(
		'c_id',
		'c_name',
		array(
			'class'=&gt;'CButtonColumn',
		),
	),
)); ?&gt;</pre>
<p>На его место в <code>views/countries/admin.php</code> добавляем следующий код.</p>
<pre class="brush: php">&lt;?php
$this-&gt;renderPartial('admingrid', array('model'=&gt;$model));
?&gt;</pre>
<p>Идея в том, что если мы получим ajax запрос, то возвращать будем только содержимое <code>admingrid.php</code>, если запрос будет обычный – всю страницу.</p>
<p>Теперь переделываем метод <code>actionAdmin</code> контроллера.</p>
<pre class="brush: php">public function actionAdmin()
{
	$model=new Countries('search');
	$model-&gt;unsetAttributes();  // clear any default values
	if(isset($_GET['Countries']))
		$model-&gt;attributes=$_GET['Countries'];

	if (isset($_GET['ajax'])) {
		$this-&gt;renderPartial('admingrid',array(
			'model'=&gt;$model,
		));
	}
	else {
		$this-&gt;render('admin',array(
			'model'=&gt;$model,
		));
	}
}</pre>
<p>Здесь добавлена дополнительная проверка (строка 8). Класс <code>CGridView</code> имеет свойство <code>ajaxVar</code>, которое по-умолчанию равно ajax. Это свойство задает имя GET параметра, который указывает, что данный запрос является ajax запросом.</p>
<p>Если этот параметр установлен, мы используем метод <code>renderPartial</code>, который возвращает браузеру только содержимое представления <code>admingrid</code>. В противном случае, вызываем метод render, который формирует всю страницу целиком.</p>
<p>Внешне в работе страницы ничего не изменилось, но если вы посмотрите с помощью firebug’а ответы сервера на ajax запросы, то разница будет заметной.</p>
<p>Теперь сделаем <strong>просмотр записей с помощью AJAX</strong>.</p>
<p>По-умолчанию, если мы нажмем на кнопку просмотра (в последней колонке таблицы), то мы попадем на страницу соответствующей записи.</p>
<div id="attachment_1106" class="wp-caption alignnone" style="width: 123px"><img src="http://www.simplecoding.org/wp-content/uploads/2010/07/grid_buttons.png" alt="grid buttons" title="grid buttons" width="113" height="102" class="size-full wp-image-1106" /><p class="wp-caption-text"> </p></div>
<p>Мы изменим это поведение. Добавим под таблицей блок, в котором будут отображаться подробные сведения о выбранной записи, т.е. содержимое представления <code>views/countries/admingrid.php</code>.</p>
<p>Чтобы не менять стандартное поведение кнопок в таблице мы добавим к ней ещё один столбец со ссылками «<code>Предпросмотр</code>».</p>
<p>При клике по этой ссылке под таблицей появится подробная информация о данной записи.</p>
<p>Приступим.</p>
<p>Добавляем новую колонку в таблицу. Для этого изменим файл <code>admingrid.php</code> следующим образом.</p>
<pre class="brush: php">&lt;?php
$this-&gt;widget('zii.widgets.grid.CGridView', array(
	'id'=&gt;'countries-grid',
	'dataProvider'=&gt;$model-&gt;search(),
	'filter'=&gt;$model,
	'afterAjaxUpdate'=&gt;'function(id, data) {$.fn.setPreviewLinksHandler(id, data);}',
	'columns'=&gt;array(
		'c_id',
		'c_name',
		array(
			'class'=&gt;'CButtonColumn',
		),
		array(
			'type'=&gt;'raw',
			'value'=&gt;'CHtml::link(&quot;Предпросмотр&quot;, array(&quot;countries/view&quot;, &quot;id&quot;=&gt;$data-&gt;c_id, &quot;ajax&quot;=&gt;&quot;preview&quot;), array(&quot;name&quot;=&gt;&quot;previewLink&quot;))',
		),
	),
)); ?&gt;</pre>
<p>Здесь есть нюансы. Во-первых, при добавлении столбца необходимо указать, что он имеет тип <code>raw</code>, иначе по-умолчанию <strong>Yii</strong> применит фильтры к его содержимому.</p>
<p>Во-вторых, нам нужно назначить каждой ссылке обработчик события <code>onClick</code>. Причем, сделать это необходимо как при первоначальной загрузке страницы, так и при «перелистовании».</p>
<p>Для этого мы используем свойство <code>afterAjaxUpdate</code> (строка 6). Ему присваиваем JavaScript функцию, которая будет вызвана после обновления таблицы с помощью ajax метода.</p>
<p>Теперь напишем функцию <code>setPreviewLinksHandler</code>. Для этого создаём файл <code>js/previewlinks.js</code>.</p>
<pre class="brush: php">$(function() {
	$.fn.setPreviewLinksHandler = function(id, data) {
		$(&quot;a[name=previewLink]&quot;).click(function() {
			var link = $(this);
			$.get(link.attr('href'), function(data) {
				$('#preview').html(data);
			});
			return false;
		});
	};
	$.fn.setPreviewLinksHandler();
});</pre>
<p>Эта функция получает в качестве параметров <code>id</code> блока с таблицей и её содержимое (<code>data</code>). Но, т.к. содержимое таблицы мы менять не будем, то и эти параметры нам не нужны.</p>
<p>Наша функция <code>setPreviewLinksHandler</code> ищет все ссылки, у которых атрибут name равен <code>previewLink</code> и назначает им обработчик события <code>onClick</code>.</p>
<p>Этот обработчик формирует и отправляет ajax запрос, который возвращает подробную информацию о записи.</p>
<p>Тут обратите внимание, что при отправке ajax запроса используется атрибут <code>href</code> ссылки, таким образом, при отключенном JS клик по ссылке будет отправлять пользователя на страницу с подробной информацией о записи.</p>
<p>Рассмотрим метод <code>actionView</code> контроллера.</p>
<pre class="brush: php">public function actionView()
{
	if (isset($_GET['ajax'])) {
		$this-&gt;renderPartial('viewrow',array(
			'model'=&gt;$this-&gt;loadModel(),
		));
	}
	else {
		$this-&gt;render('view',array(
			'model'=&gt;$this-&gt;loadModel(),
		));
	}
}</pre>
<p>Здесь мы проверяем, есть ли в параметрах запроса атрибут <code>ajax</code>, и если он есть, показываем представление <code>viewrow</code>. При этом используется метод <code>renderPartial</code>. В противном случае, просто показываем представление <code>view</code>.</p>
<p><code>viewrow.php</code> создаём по тому же принципу, что и <code>admingrid.php</code>. Перемещаем код создания виджета <code>CDetailView</code> в <code>viewrow.php</code>.</p>
<pre class="brush: php">&lt;?php $this-&gt;widget('zii.widgets.CDetailView', array(
	'data'=&gt;$model,
	'attributes'=&gt;array(
		'c_id',
		'c_name',
	),
)); ?&gt;</pre>
<p>А в <code>view.php</code> добавим</p>
<pre class="brush: php">&lt;?php $this-&gt;renderPartial('viewrow', array('model'=&gt;$model)); ?&gt;</pre>
<p>В результате этих манипуляций подробные сведения о записи будут появляться под таблицей при клике по ссылке «Предпросмотр».</p>
<p>Я надеюсь, что никого не запутал своими объяснениями <img src='http://www.simplecoding.org/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' />  Чтобы вам удобнее было экспериментировать, выкладываю архив с исходниками этого примера и дампом базы (предварительно вам нужно будет создать приложение Yii).</p>
<p><a href='http://www.simplecoding.org/wp-content/uploads/2010/07/yii_grid_countries.zip'><img src="http://www.simplecoding.org/wp-content/themes/three_cols/images/download_btn_blue.png" alt="архив с исходным кодом" /></a></p>
<p>Если есть вопросы или замечания, пишите! Особенно интересуют альтернативные варианты решения таких задач <img src='http://www.simplecoding.org/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p><strong>Интересно почитать</strong></p>
<p>Компания Intelsib предлагает <a href="http://www.intelsib.ru">продвижение сайта в интернете, поисковиках</a>, а также его аудит.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.simplecoding.org/cgridview-chast-vtoraya-ajax.html/feed</wfw:commentRss>
		<slash:comments>27</slash:comments>
		</item>
		<item>
		<title>Yii PHP фреймворк: оформление административных страниц</title>
		<link>http://www.simplecoding.org/yii-php-frejmvork-oformlenie-administrativnyx-stranic.html</link>
		<comments>http://www.simplecoding.org/yii-php-frejmvork-oformlenie-administrativnyx-stranic.html#comments</comments>
		<pubDate>Tue, 06 Jul 2010 07:33:36 +0000</pubDate>
		<dc:creator>Владимир</dc:creator>
				<category><![CDATA[HTML]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web разработка]]></category>
		<category><![CDATA[Yii]]></category>

		<guid isPermaLink="false">http://www.simplecoding.org/?p=1094</guid>
		<description><![CDATA[Одной из наиболее мощных возможностей фреймворка Yii является генерация кода. Она позволяет сразу после создания таблицы в базе данных получить файлы модели, контроллера и представлений. Т.е. весь необходимый код для выполнения CRUD операций. Но, естественно, код формируется по стандартному шаблону, дизайн которого вам, возможно, захочется изменить. Взгляните на обычную страницу управления записями, сформированную с помощью [...]]]></description>
			<content:encoded><![CDATA[<div id="attachment_1095" class="wp-caption alignnone" style="width: 310px"><img src="http://www.simplecoding.org/wp-content/uploads/2010/07/yii_grid_view.png" alt="yii grid view" title="yii grid view" width="300" height="169" style="float:left" class="size-full wp-image-1095" /><p class="wp-caption-text"> </p></div>
<p>Одной из наиболее мощных возможностей <strong>фреймворка Yii</strong> является генерация кода. Она позволяет сразу после создания таблицы в базе данных получить файлы модели, контроллера и представлений. Т.е. весь необходимый код для выполнения CRUD операций.</p>
<p>Но, естественно, код формируется по стандартному шаблону, дизайн которого вам, возможно, захочется изменить.</p>
<p>Взгляните на обычную страницу управления записями, сформированную с помощью компонента <a href="http://www.yiiframework.com/doc/api/CGridView">CGridView</a>.<br />
<span id="more-1094"></span><br />
<div id="attachment_1097" class="wp-caption alignnone" style="width: 460px"><a href="http://www.simplecoding.org/wp-content/uploads/2010/07/basic_grid.png" target="_blank"><img src="http://www.simplecoding.org/wp-content/uploads/2010/07/basic_grid_thumb.png" alt="basic grid thumb" title="basic grid thumb" width="450" height="293" class="size-full wp-image-1097" /></a><p class="wp-caption-text"> </p></div></p>
<p>Такая страница доступна по адресу<br />
<code>sitename.domen?r=controller/admin</code></p>
<p>Она состоит из трех частей.</p>
<p>1) Строки с номерами показанных записей и их количеством (<code>summary</code>).</p>
<p>2) Таблицы с данными (<code>items</code>).</p>
<p>3) Листалки (<code>pager</code>). Если записей в таблице меньше, чем отображается на странице, этот элемент показан не будет.</p>
<p>Теперь взгляните на код, который создает такую таблицу.</p>
<pre class="brush: php">&lt;?php $this-&gt;widget('zii.widgets.grid.CGridView', array(
	'id'=&gt;'cities-grid',
	'dataProvider'=&gt;$model-&gt;search(),
	'filter'=&gt;$model,
	'columns'=&gt;array(
		'id',
		'city',
		'country',
		array(
			'class'=&gt;'CButtonColumn',
		),
	),
)); ?&gt;</pre>
<p>Источником данных служит объект <code>CActiveDataProvider</code>, который создается в контроллере. Но сейчас речь не о данных.</p>
<p>Попробуем <strong>изменить порядок вывода основных элементов</strong>.</p>
<p><code>CGridView</code> имеет параметр <code>template</code>, который позволяет управлять выводом основных элементов страницы. Этот параметр принимает строку, в которой нужно перечислить необходимые элементы страницы.</p>
<p>Например, следующий код покажет листалку сверху и снизу страницы.</p>
<pre class="brush: php">&lt;?php $this-&gt;widget('zii.widgets.grid.CGridView', array(
	…,
	'template'=&gt;'{summary} {pager} {items} {pager}',
	'columns'=&gt;array(
		…,
	),
)); ?&gt;</pre>
<p>В шаблон можно вставлять произвольный текст и теги, но это не очень удобный вариант изменения разметки страницы. Лучше менять разметку внутри самих элементов и/или использовать CSS стили.</p>
<p>Если нужно <strong>изменить дизайн</strong>, то я бы рекомендовал делать в следующей последовательности.</p>
<p>1) Попытаться добиться результата, не меняя разметку, с помощью CSS стилей. Разметка, которую создаёт фреймворк, достаточна простая и соответствует стандартам.</p>
<p>2) Если необходимо, изменить разметку.</p>
<p>Рассмотрим первый вариант – <strong>использование своих CSS стилей</strong>.</p>
<p>По-умолчанию, для оформления <code>CGridView</code> используются следующие CSS файлы:</p>
<p>1) Для оформления таблицы <code>framework/zii/widgets/assets/gridview/styles.css</code> (он автоматически копируется в папку <code>DOCUMENT_ROOT/accets/номер/gridview/styles.css</code>).</p>
<p>2) Для оформления листалки <code>framework/web/widgets/pagers/pager.css</code> (он копируется в <code>assets/номер/pager.css</code>).</p>
<p>Допустим, ваши стили находятся в файлах <code>gridview-styles.css</code> и <code>pager-styles.css</code>, которые расположены в папке <code>DOCUMENT_ROOT/css</code>.</p>
<p>Подключаем файл со стилями таблицы, для этого передаем ссылку на файл в параметре <code>cssFile</code>.</p>
<pre class="brush: php">&lt;?php $this-&gt;widget('zii.widgets.grid.CGridView', array(
	…,
	'cssFile'=&gt;Yii::app()-&gt;getBaseUrl(true).'/css/admin-styles.css',
	'columns'=&gt;array(
		…,
	),
)); ?&gt;</pre>
<p>В результате, фреймворк будет загружать ваш файл вместо стандартного.</p>
<p>Кроме того, есть несколько параметров (<code>itemsCssClass</code>, <code>rowCssClass</code>, <code>summaryCssClass</code> и др.), которые позволяют указать ваши имена CSS классов. Но, вы можете использовать и стандартные имена классов, например, взять стандартный файл со стилями и переделывать его под свой дизайн.</p>
<p><strong>Установка стилей для листалки</strong> (<code>pager</code>) немного отличается.</p>
<p>Из <code>CGridView</code> нужно получить доступ к атрибуту pager и устанавливать параметры для него.</p>
<pre class="brush: php">&lt;?php $this-&gt;widget('zii.widgets.grid.CGridView', array(
	…,
	'pager'=&gt;array('cssFile'=&gt;Yii::app()-&gt;getBaseUrl(true).'/css/pager-styles.css'),	'columns'=&gt;array(
		…,
	),
)); ?&gt;</pre>
<p>Код похожий, но параметр <code>cssFile</code> передается в массиве и присваивается атрибуту <code>pager</code>.</p>
<p>Тут возникает интересный вопрос: «Какие еще параметры мы можем передать в этом массиве?»</p>
<p>Любые атрибуты класса <code>CBasePager</code>, а также его потомков, в том числе и имя класса-потомка, который используется для создания листалки.</p>
<p>В дистрибутив входит два таких класса <code>CLinkPager</code> (создает список ссылок с номерами страниц, используется по-умолчанию) и <code>CListPager</code> (создает выпадающий список с номерами страниц).</p>
<p>Заменить <code>CLinkPager</code> на <code>CListPager</code> можно так.</p>
<pre class="brush: php">…
	'pager'=&gt;array('class'=&gt;'CListPager'),
…</pre>
<p>Тут мы подходим к вопросу <strong>изменения разметки</strong>.</p>
<p>Допустим, нам нужно создать листалку в которой элементы страниц будут находиться внутри тегов <code>span</code>, а не <code>li</code>, как выводит <code>CLinkPager</code>.</p>
<p>Можно, конечно, создать класс, производный от <code>CBasePager</code>, но в данном случае будет удобнее наследовать <code>CLinkPager</code>.</p>
<p>У меня получился такой код (файл <code>protected/components /SpanLinkPager.php</code>).</p>
<pre class="brush: php">class SpanLinkPager extends CLinkPager {

	public function run()
	{
		$buttons=$this-&gt;createPageButtons();
		if(empty($buttons))
			return;
		$this-&gt;registerClientScript();
		echo $this-&gt;header;
		echo CHtml::tag('div',$this-&gt;htmlOptions,implode(&quot;\n&quot;,$buttons));
		echo $this-&gt;footer;
	}

	protected function createPageButton($label,$page,$class,$hidden,$selected)
	{
		if($hidden || $selected)
			$class.=' '.($hidden ? self::CSS_HIDDEN_PAGE : self::CSS_SELECTED_PAGE);
		return '&lt;span class=&quot;'.$class.'&quot;&gt;'.CHtml::link($label,$this-&gt;createPageUrl($page)).'&lt;/span&gt;';
	}
}</pre>
<p>Я просто нашел в <code>CLinkPager</code> методы, в который формируется разметка и переопределил их. Т.е. код остался тот же, просто <code>ul</code> заменил на <code>div</code> (строка 12), а <code>li</code> &#8211; на <code>span</code> (строка 20).</p>
<p>Используя такой же подход можно переопределить класс <code>CGridView</code> и изменить разметку необходимым вам образом. Вот список основных методов, которые формируют разметку <code>CGridView</code>:<br />
<code>renderItems<br />
renderTableHeader<br />
renderTableFooter<br />
renderTableBody<br />
renderEmptyText<br />
renderFilter<br />
renderTableRow<br />
renderSummary</code> (находится в <code>CBaseListView</code>, который является предком <code>CGridView</code>)</p>
<p>На этом мы остановимся. Конечно, в этой статье рассмотрены далеко не все нюансы использования компонента <code>CGridView</code>. Например, он позволяет управлять выводом данных, а также использовать скины, но об этом как-нибудь в другой раз <img src='http://www.simplecoding.org/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Если у вас возникли вопросы или есть замечания, пишите, постараюсь ответить.</p>
<p>До встречи!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.simplecoding.org/yii-php-frejmvork-oformlenie-administrativnyx-stranic.html/feed</wfw:commentRss>
		<slash:comments>20</slash:comments>
		</item>
		<item>
		<title>Yii PHP framework: создание запросов с условием IN</title>
		<link>http://www.simplecoding.org/yii-php-framework-sozdanie-zaprosov-s-usloviem-in.html</link>
		<comments>http://www.simplecoding.org/yii-php-framework-sozdanie-zaprosov-s-usloviem-in.html#comments</comments>
		<pubDate>Sat, 03 Jul 2010 10:27:22 +0000</pubDate>
		<dc:creator>Владимир</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web разработка]]></category>
		<category><![CDATA[Yii]]></category>

		<guid isPermaLink="false">http://www.simplecoding.org/?p=1092</guid>
		<description><![CDATA[Последнее время мне довольно часто приходится работать с фреймворком Yii. И иногда возникают вопросы, ответы на которые не очевидны (во всяком случае для меня). Попробую объяснить. Сам фреймворк, на мой взгляд, достаточно удобный и документация подробная. Но классов много, и формат передачи данных их методам часто сильно влияет на результат. Рассмотрим небольшой пример – использование [...]]]></description>
			<content:encoded><![CDATA[<div id="attachment_1093" class="wp-caption alignnone" style="width: 294px"><img src="http://www.simplecoding.org/wp-content/uploads/2010/07/yii_in_statement.png" alt="yii in condition" title="yii in condition" width="284" height="148" style="float:left" class="size-full wp-image-1093" /><p class="wp-caption-text"> </p></div>
<p>Последнее время мне довольно часто приходится работать с <strong>фреймворком Yii</strong>. И иногда возникают вопросы, ответы на которые не очевидны (во всяком случае для меня).</p>
<p>Попробую объяснить. Сам фреймворк, на мой взгляд, достаточно удобный и документация подробная. Но классов много, и формат передачи данных их методам часто сильно влияет на результат.</p>
<p>Рассмотрим небольшой пример – <strong>использование оператора IN</strong>.</p>
<p>Т.е. нужно сформировать примерно такой запрос.</p>
<pre class="brush: sql">SELECT * FROM tbl_users WHERE id IN (1, 2, 3)</pre>
<p><span id="more-1092"></span><br />
В <a href="http://yiiframework.ru/doc/guide/ru/index">Полном руководстве</a> на эту тему информации я не нашел, а копаться в методах <a href="http://www.yiiframework.com/doc/api/CActiveRecord">CActiveRecord</a> времени не было.</p>
<p>Вообще, я очень не люблю такие ситуации. С одной стороны, понятно как нужно написать запрос, с другой – хочется сделать это красиво, используя встроенную библиотеку. Копаться в документации не хочется, ведь на это уходит время, а запрос можно составить за считанные минуты. Но не все так просто.</p>
<p>Сначала покажу <strong>решение «в лоб»</strong>.</p>
<pre class="brush: php">$ids = array(1, 2, 3);

$dataProvider=new CActiveDataProvider('User',array(
	'criteria'=&gt;array(
		'condition'=&gt;'id IN ('.implode(',', $ids).')',
	)
));

$this-&gt;render('admin',array(
	'model'=&gt;$dataProvider,
));</pre>
<p>Тут мы формируем массив со значениями, которые будут перечислены после <code>IN</code>. А затем, с помощью функции <code>implode</code> преобразуем его в строку и просто вставляем в запрос.</p>
<p><strong>Недостатки</strong> такого подхода очевидны.</p>
<p><em>Во-первых</em>, выглядит код не очень красиво.</p>
<p><em>Во-вторых</em>, в реальном приложении значения для массива <code>$ids</code> будут откуда-то поступать, возможно, из параметров запроса. И если массив <code>$ids</code> окажется пустым, то при выполнении запроса возникнет ошибка. </p>
<p><em>В-третьих</em>, полученные значения нужно проверить. А написать что-то вроде</p>
<pre class="brush: php">'params'=&gt;array(':id'=&gt;implode(', ', $ids)),</pre>
<p>не получится, т.к. результат работы функции <code>implode</code> будет взят в кавычки. Т.е. проверку необходимо сделать предварительно.</p>
<p>Как видите, возникают проблемы практически на пустом месте. Нужно написать код проверок, решить, что делать с пустым массивом, затем протестировать этот код. И все ради решения тривиальной задачи.</p>
<p><strong>Решение с помощью библиотеки Yii.</strong></p>
<p>Оно выглядит гораздо проще.</p>
<pre class="brush: php">$model = User::model()-&gt;findAllByAttributes(array('id'=&gt;array(1, 2, 3)));
$dataProvider=new CActiveDataProvider('User');
$dataProvider-&gt;setData($model);

$this-&gt;render('index',array(
	'dataProvider'=&gt;$dataProvider,
));</pre>
<p>Метод <code>findAllByAttributes</code> класса <code>CActiveRecord</code> позволяет искать записи в БД по названию поля и значению. При этом если значение является массивом, то используется оператор <code>IN</code>.</p>
<p>Этот же самый код можно записать немного иначе.</p>
<pre class="brush: php">$dataProvider=new CActiveDataProvider('User');
$dataProvider-&gt;criteria-&gt;addInCondition('id', array(1,2,3));

$this-&gt;render('index',array(
	'dataProvider'=&gt;$dataProvider,
));</pre>
<p>Тут уже используется метод <code>addInCondition</code> класса <code>CDbCriteria</code>. Если вы работаете с <code>CActiveDataProvider</code>, то получить доступ к объекту <code>CDbCriteria</code> можно с помощью свойства <code>criteria</code>.</p>
<p>Теперь проведем несколько экспериментов.</p>
<p>Попробуем в массиве передать строку.</p>
<pre class="brush: php">$dataProvider-&gt;criteria-&gt;addInCondition('id', array(1,2,'test'));</pre>
<p>В результате будет выполнен следующий запрос.</p>
<pre class="brush: sql">SELECT * FROM 'tbl_user' WHERE &quot;id&quot; IN (1, 2, 0)</pre>
<p>Т.е. текстовое значение было заменено нулем, что вполне логично, т.к. поле <code>id</code> имеет тип <code>INT</code>.</p>
<p>Если запрос будет выполняться к полю текстового типа, то текстовые и числовые значения будут вставлены в кавычках.</p>
<pre class="brush: php">$dataProvider-&gt;criteria-&gt;addInCondition('id', array(1, 'admin','user'));</pre>
<p>сформирует такой запрос</p>
<pre class="brush: sql">SELECT * FROM 'tbl_user' WHERE &quot;id&quot; IN ('1','admin', 'user')</pre>
<p>А если массив окажется пуст, то фреймворк гарантирует, что из таблицы не будет выбрана ни одна запись.</p>
<pre class="brush: php">$dataProvider-&gt;criteria-&gt;addInCondition('id', array());</pre>
<pre class="brush: sql">SELECT * FROM 'tbl_user' WHERE 0=1</pre>
<p>Как видите, использование встроенных библиотек всё-таки оправдывает время, потраченное на их изучение <img src='http://www.simplecoding.org/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p>Только не забывайте при работе с документацией Yii учитывать один момент. Классы библиотеки тесно связаны между собой, поэтому искать нужно не только в методах класса, с которым вы непосредственно работаете, но и в методах объектов, которые использует этот класс.</p>
<p>Так <code>CActiveDataProvider</code> использует <code>CDbCriteria</code> для формирования условий, <code>CPagination</code> – для разбивки на страницы, CSort – для сортировки и т.д.</p>
<p>Вообще, я заметил, чем больше работаешь с фреймворком, тем логичнее кажутся решения разработчиков и тем проще ориентироваться в документации <img src='http://www.simplecoding.org/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Удачи!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.simplecoding.org/yii-php-framework-sozdanie-zaprosov-s-usloviem-in.html/feed</wfw:commentRss>
		<slash:comments>16</slash:comments>
		</item>
		<item>
		<title>Yii фреймворк: получение статистики перехода по ссылкам от сервиса Bit.ly</title>
		<link>http://www.simplecoding.org/yii-frejmvork-poluchenie-statistiki-perexoda-po-ssylkam-ot-servisa-bit-ly.html</link>
		<comments>http://www.simplecoding.org/yii-frejmvork-poluchenie-statistiki-perexoda-po-ssylkam-ot-servisa-bit-ly.html#comments</comments>
		<pubDate>Tue, 15 Jun 2010 08:19:22 +0000</pubDate>
		<dc:creator>Владимир</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web разработка]]></category>
		<category><![CDATA[Yii]]></category>

		<guid isPermaLink="false">http://www.simplecoding.org/?p=1083</guid>
		<description><![CDATA[Сегодня продолжим тему использования сервиса Bit.ly вместе с PHP фреймворком Yii. В прошлой части мы рассмотрели создание коротких ссылок для страниц вымышленного сайта с новостями. В этой части мы создадим отдельную страницу, на которой будет отображаться статистика переходов по коротким ссылкам. Приступим. Шаг первый. Добавляем новое поле в модель. На данный момент у нас есть [...]]]></description>
			<content:encoded><![CDATA[<div id="attachment_1081" class="wp-caption alignnone" style="width: 270px"><img src="http://www.simplecoding.org/wp-content/uploads/2010/06/yii_bitly.png" alt="yii bitly" title="yii bitly" width="260" height="196" class="size-full wp-image-1081" style="float:left" /><p class="wp-caption-text"> </p></div>
<p>Сегодня продолжим тему использования сервиса Bit.ly вместе с PHP фреймворком Yii. В <a href="http://www.simplecoding.org/ispolzovanie-servisa-bit-ly-vmeste-s-php-frejmvorkom-yii.html">прошлой части</a> мы рассмотрели создание коротких ссылок для страниц вымышленного сайта с новостями.</p>
<p>В этой части мы создадим отдельную страницу, на которой будет отображаться <strong>статистика переходов по коротким ссылкам</strong>.</p>
<p>Приступим.<br />
<span id="more-1083"></span><br />
<strong>Шаг первый. Добавляем новое поле в модель.</strong></p>
<p>На данный момент у нас есть модель <code>News</code>, которая содержит следующие поля:</p>
<p><code>n_id</code> – первичный ключ;<br />
<code>n_title</code> – заголовок новости;<br />
<code>n_content</code> – текст новости;<br />
<code>n_short_url</code> – короткая ссылка (не обязательное поле).</p>
<p>Мы добавим ещё одно поле <code>n_clicks</code>, в которое будем записывать количество переходов по короткой ссылке.</p>
<pre class="brush: php">class News extends CActiveRecord
{
	public $n_clicks;
...</pre>
<p>Сохранять значение этого поля в базе данных не нужно, ведь оно будет очень быстро устаревать, поэтому мы будем получать его с сервиса <em>Bit.ly</em> при каждой загрузке страницы со статистикой.</p>
<p><strong>Шаг второй. Создаём метод для получения статистики.</strong></p>
<p>В прошлой части мы создали компонент <code>BitLyShortener</code> с методом получения короткой ссылки. Используем этот же компонент и добавим в него новый метод.</p>
<pre class="brush: php">public static function getStatistics(&amp;$models) {
	//если массив пуст - не делаем ничего
	if (0 == count($models)) {
		return;
	}
	/*
	 В запросе к Bit.ly можно передать до 15 ссылок.
	 Поэтому сначала формируем общую часть запроса, а
	 затем - добавляем в цикле ссылки, для которых
	 нужно получить статистику.
	*/
	$requestBase = 'http://api.bit.ly/v3/clicks?'
		.'login='.self::LOGIN
		.'&amp;apiKey='.self::APIKEY
		.'&amp;format=json';

	//разбиваем массив с моделями на блоки по 15 элементов
	$modelsChunks = array_chunk($models, 15, true);
	//для каждого блока формируем и отправляем запрос
	foreach ($modelsChunks as $i =&gt; $chunk) {
		$request = $requestBase;
		foreach ($chunk as $key =&gt; $model) {
			$request .= '&amp;shortUrl='.urlencode($model-&gt;n_short_url);
		}
		$response = file_get_contents($request);
		$res = json_decode($response, true);
		//сохраняем результаты
		foreach ($chunk as $key =&gt; $model) {
			$models[$key]-&gt;n_clicks = $res['data']['clicks'][$key - $i*15]['global_clicks'];
		}
	}
}</pre>
<p>Принцип получения статистики такой же, как и принцип создания короткой ссылки. Нужно отправить специальным образом сформированный запрос. Но есть нюанс.</p>
<p>Сервис Bit.ly накладывает ограничение на количество ссылок, для которых можно получить статистику. На данный момент обрабатывается не более 15 ссылок на запрос.</p>
<p>Поэтому мы разбиваем массив с короткими ссылками на блоки по 15 штук, и для каждого блока отправляем отдельный запрос.</p>
<p>Вообще сервис возвращает несколько значений</p>
<pre class="brush: php">&quot;clicks&quot;: [
    {
        &quot;short_url&quot;: &quot;http://tcrn.ch/a4MSUH&quot;, //короткая ссылка
        &quot;global_hash&quot;: &quot;bWw49z&quot;, //глобальный хеш идентификатора bit.ly
        &quot;user_clicks&quot;: 0, //количество кликов по данной ссылке
        &quot;user_hash&quot;: &quot;a4MSUH&quot;, //идентификатор пользователя bit.ly
        &quot;global_clicks&quot;: 1105 //количество кликов по всем коротким ссылкам, которые соответствуют данному полному URL
        },</pre>
<p>Из всех этих параметров нам нужен только <code>global_clicks</code>. Его мы и записываем в исходный массив с новостями.</p>
<p><strong>Шаг третий. Создаём страницу со статистикой.</strong></p>
<p>Для этого добавим в контроллер метод <code>actionStatistics</code></p>
<pre class="brush: php">public function actionStatistics() {
	$criteria = new CDbCriteria;
	$pages = new CPagination(News::model()-&gt;count($criteria));
	$pages-&gt;pageSize = self::PAGE_SIZE;

	$models = News::model()-&gt;findAll($criteria);

	//получаем данные о посещаемости от Bit.ly
	BitLyShortener::getStatistics($models);

	$this-&gt;render('statistics', array('models'=&gt;$models));
}</pre>
<p>И создаём представление <code>protected/views/news/statistics.php</code></p>
<pre class="brush: php">&lt;?php
foreach ($models as $model) {
?&gt;
    &lt;p&gt;
        &lt;strong&gt;&lt;?php echo $model-&gt;n_title; ?&gt;&lt;/strong&gt;&lt;br /&gt;
        Короткая ссылка: &lt;?php echo $model-&gt;n_short_url; ?&gt;&lt;br /&gt;
        Количество переходов: &lt;?php echo $model-&gt;n_clicks; ?&gt;
    &lt;/p&gt;
&lt;?php
}
//TODO добавить листалку страниц

//end of statistics.php</pre>
<p>В результате, по адресу <code>index.php?r=news/statistics</code> должна появиться страница со статистикой.</p>
<p>Как видите, ничего сложного. Главное, не ошибиться при составлении запроса для Bit.ly.</p>
<p>Архив с файлами для этого примера (необходимо распаковать в папку protected).</p>
<p><a href='http://www.simplecoding.org/wp-content/uploads/2010/06/bit_ly_statistics.zip'><img src="http://www.simplecoding.org/wp-content/themes/three_cols/images/download_btn_blue.png" alt="архив с исходным кодом" /></a></p>
<p>Все вопросы и замечания пишите в комментариях. Постараюсь ответить <img src='http://www.simplecoding.org/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p><strong>Интересно почитать</strong></p>
<p>В магазине gamesdealer.ru  есть <a href="http://gamesdealer.ru/">все для покера:</a> карты, фишки и покерные наборы.</p>
<p>Магазин Irobot-home.ru: лучшие <a href="http://www.irobot-home.ru/">роботы пылесосы</a> от iRobot.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.simplecoding.org/yii-frejmvork-poluchenie-statistiki-perexoda-po-ssylkam-ot-servisa-bit-ly.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Использование сервиса Bit.ly вместе с PHP фреймворком Yii</title>
		<link>http://www.simplecoding.org/ispolzovanie-servisa-bit-ly-vmeste-s-php-frejmvorkom-yii.html</link>
		<comments>http://www.simplecoding.org/ispolzovanie-servisa-bit-ly-vmeste-s-php-frejmvorkom-yii.html#comments</comments>
		<pubDate>Sat, 12 Jun 2010 17:42:09 +0000</pubDate>
		<dc:creator>Владимир</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web разработка]]></category>
		<category><![CDATA[Yii]]></category>

		<guid isPermaLink="false">http://www.simplecoding.org/?p=1080</guid>
		<description><![CDATA[Думаю, о твиттере и о том, какую роль в нем играют короткие ссылки, слышали все. В принципе, можно вообще не заморачиваться, т.к. twitter сам создаст их для вас, но при таком подходе возникнет несколько проблем. Во-первых, длина ссылки и текста может превысить 140 символов. Т.е. ссылку лучше сразу сократить. Во-вторых, будет сложнее отслеживать статистику переходов. [...]]]></description>
			<content:encoded><![CDATA[<div id="attachment_1081" class="wp-caption alignnone" style="width: 270px"><img src="http://www.simplecoding.org/wp-content/uploads/2010/06/yii_bitly.png" alt="yii bitly" title="yii bitly" width="260" height="196" class="size-full wp-image-1081" style="float:left" /><p class="wp-caption-text"> </p></div>
<p>Думаю, о твиттере и о том, какую роль в нем играют <strong>короткие ссылки</strong>, слышали все. В принципе, можно вообще не заморачиваться, т.к. <strong>twitter</strong> сам создаст их для вас, но при таком подходе возникнет несколько проблем.</p>
<p><em>Во-первых</em>, длина ссылки и текста может превысить 140 символов. Т.е. ссылку лучше сразу сократить.</p>
<p><em>Во-вторых</em>, будет сложнее отслеживать статистику переходов.</p>
<p>Поэтому гораздо удобнее использовать специализированные сервисы, например, <strong>Bit.ly</strong>. Этот сервис предоставляет <a href="http://code.google.com/p/bitly-api/wiki/ApiDocumentation">API</a> как для создания ссылок, так и для получения статистики переходов по ним.</p>
<p>Теперь рассмотрим небольшой пример.<br />
<span id="more-1080"></span><br />
Допустим, вы <strong>разрабатываете сайт с использованием PHP фреймворка Yii</strong>. На котором у вас есть раздел с новостями. Необходимо на странице каждой новости создать кнопку «Опубликовать в твиттере», а текст сообщения должен состоять из заголовка новости и короткой ссылки на неё.</p>
<p>Код создания самой кнопки мы сейчас рассматривать не будем, т.к. это немного другая тема. Мы напишем компонент, позволяющий получать сокращенный URL страницы, и рассмотрим его подключение к фреймворку.</p>
<p><strong>Шаг первый. Подготовим площадку для экспериментов.</strong></p>
<p>1) Создаем приложение</p>
<p><code>yiic webapp .</code></p>
<p>2) Создаем таблицу в БД. Я использовал стандартную sqlite базу.</p>
<p>Название таблицы <code>tbl_news</code></p>
<p>Поля:</p>
<p><code>n_id</code> – первичный ключ;<br />
<code>n_title</code> – заголовок новости;<br />
<code>n_content</code> – текст новости;<br />
<code>n_short_url</code> – короткая ссылка (не обязательное поле).</p>
<p>3) Создаем контроллер и модель</p>
<p>Для этого последовательно выполняем три команды</p>
<p><code>yiic shell<br />
model News tbl_news<br />
crud News</code></p>
<p>Теперь у нас есть работающий сайт. Страница с новостями будет доступна по адресу.</p>
<p><code>site_name.domen/index.php?r=news/index</code></p>
<p>На этой странице в сайдбаре будут ссылки на страницы создания и управления новостями.</p>
<p><strong>Шаг второй. Регистрируемся на сервисе Bit.ly.</strong></p>
<p>В принципе, короткие ссылки можно получить и без регистрации, но тогда вы не сможете смотреть статистику.</p>
<p>Регистрация осуществляется на <a href="http://bit.ly/account/register">этой</a> странице.</p>
<p>Затем необходимо получить <a href="http://bit.ly/account/your_api_key">API-ключ</a>.</p>
<p>Ваши логин и ключ API будут использоваться при всех обращениях к сервису.</p>
<p><strong>Шаг третий. Если необходимо, настраиваем urlManager.</strong></p>
<p>Этот компонент позволяет превращать ссылки вида<br />
<code>localhost/sites/yii_bit_ly/public_html/index.php?r=news/view&amp;id=1</code><br />
в<br />
<code>localhost/sites/yii_bit_ly/public_html/index.php/news/1</code></p>
<p>Настройка выполняется в файле <code>protected/config/main.php</code>, подробнее о ней можно почитать в статье <a href="http://yiiframework.ru/doc/guide/ru/topics.url">Красивые адреса URL</a>.</p>
<p>Дело в том, что если вы измените формат ссылок, то сокращенные ссылки нужно будет создавать заново, поэтому лучше определиться с ним заранее.</p>
<p><strong>Шаг четвертый. Создаём компонент для работы с сервисом.</strong></p>
<p>Назовем его <code>BitLyShortener</code>. Он должен находиться в файле (<code>protected/components/BitLyShortener.php</code>).</p>
<pre class="brush: php">&lt;?php
/**
 * Этот компонент предназначен для создания ссылок с помощью
 * сервиса Bit.ly
 */
class BitLyShortener extends CComponent {
    const LOGIN = 'ваш_логин';
    const APIKEY = 'ваш_API_ключ';

    /**
     * Укорачивает заданнй URL с помощью сервиса Bit.ly
     *
     * @param $fullUrl string - URL для которого нужно получить короткую ссылку
     * @return string короткая ссылка
     */
    public static function getShortUrl($fullUrl) {
        //формируем запрос
        $request = 'http://api.bit.ly/v3/shorten?'
            .'login='.self::LOGIN
            .'&amp;apiKey='.self::APIKEY
            .'&amp;longUrl='.urlencode($fullUrl)
            .'&amp;format=json';

        //отправляем запрос
        $response = file_get_contents($request);
        $res = json_decode($response, true);

        return $res['data']['url'];
    }
}

//end of BitLyshortener.php</pre>
<p>Архив с этим компонентом</p>
<p><a href='http://www.simplecoding.org/wp-content/uploads/2010/06/BitLyShortener.zip'><img src="http://www.simplecoding.org/wp-content/themes/three_cols/images/download_btn_blue.png" alt="архив с исходным кодом" /></a></p>
<p>Как видите, перед использованием нужно указать ваши логин и API ключ для работы с Bit.ly. Затем, для получения короткой ссылки достаточно вызвать метод <code>getShortUrl</code> и передать ему ссылку страницы в первом параметре.</p>
<p>API у Bit.ly достаточно прост. Для получения короткой ссылки достаточно сформировать и отправить обычный HTTP запрос. Ответ можно получить как в JSON, так и в XML формате. На мой взгляд, работать с JSON немного проще, но это не принципиально.</p>
<p>В случае успешного создания короткой ссылки сервис возвращает следующие данные</p>
<pre class="brush: php">array
  'status_code' =&gt; int 200
  'status_txt' =&gt; string 'OK' (length=2)
  'data' =&gt;
    array
      'long_url' =&gt; string 'http://site_name.domen/index.php?r=news/view&amp;id=2' (length=72)
      'url' =&gt; string 'http://bit.ly/short_link' (length=20)
      'hash' =&gt; string 'c86mVl' (length=6)
      'global_hash' =&gt; string '9mLuyJ' (length=6)
      'new_hash' =&gt; int 1</pre>
<p><strong>Шаг пятый. Добавляем код создания ссылки в контроллер.</strong></p>
<p>В случае с фреймворком Yii ссылку сокращать удобнее всего в методе <code>loadModel()</code> контроллера <code>News</code>, т.к. сразу после создания новости будет выполнен редирект на новую страницы и, соответственно, вызван метод <code>loadModel()</code>.</p>
<pre class="brush: php">public function loadModel()
{
	if($this-&gt;_model===null)
	{
		if(isset($_GET['id']))
			$this-&gt;_model=News::model()-&gt;findbyPk($_GET['id']);
			//проверяем, создана ли короткая ссылка для данной новости
			if (!isset($this-&gt;_model-&gt;n_short_url) || $this-&gt;_model-&gt;n_short_url == '') {
				//получаем адрес текущей страницы
				$request = Yii::app()-&gt;getRequest();
				$link = $request-&gt;getHostInfo().$request-&gt;getUrl();
				$this-&gt;_model-&gt;n_short_url = BitLyShortener::getShortUrl($link);
				//сохраняем короткую ссылку
				$this-&gt;_model-&gt;save();
			}
		if($this-&gt;_model===null)
			throw new CHttpException(404,'The requested page does not exist.');
	}
	return $this-&gt;_model;
}</pre>
<p>Здесь мы проверяем, создана ли для данной записи короткая ссылка, и если нет – создаём её. Предварительно получаем URL текущей страницы. Затем, присваиваем короткую ссылку атрибуту <code>n_short_url</code> и сохраняем модель.</p>
<p>Таким образом, запрос на создание короткой ссылки будет отправляться только один раз для каждой страницы.</p>
<p>Вывести полученную ссылку можно точно так же, как и любой другой атрибут модели.</p>
<p>Вот и всё! Если у вас появились вопросы или есть замечания, пишите, обсудим <img src='http://www.simplecoding.org/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p><strong>Интересно почитать</strong></p>
<p>Лучшие предложения на <a href="http://www.skladmetalla.ru/">металлопрокат в москве</a> на<br />
skladmetalla.ru  .</p>
<p>Уют и тепло ваше квартире дадут <a href="http://mc-140-500.ru/">чугунные радиаторы</a> для надежного отопления вашей квартиры.</p>
<p>Манго фитнес-центр и <a href="http://salonmango.ru/">салон красоты в цао</a>, все под одной крышей.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.simplecoding.org/ispolzovanie-servisa-bit-ly-vmeste-s-php-frejmvorkom-yii.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Ограничения фреймворков (на примере Yii)</title>
		<link>http://www.simplecoding.org/ogranicheniya-frejmvorkov-na-primere-yii.html</link>
		<comments>http://www.simplecoding.org/ogranicheniya-frejmvorkov-na-primere-yii.html#comments</comments>
		<pubDate>Mon, 31 May 2010 13:38:04 +0000</pubDate>
		<dc:creator>Владимир</dc:creator>
				<category><![CDATA[Ajax]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Web разработка]]></category>
		<category><![CDATA[Yii]]></category>

		<guid isPermaLink="false">http://www.simplecoding.org/?p=1073</guid>
		<description><![CDATA[О том, что фреймворки позволяют сократить время разработки, знают все. Но иногда при этом появляются самые неожиданные ограничения. По-идее, так и должно быть. Каким бы универсальным не пытались сделать фреймворк разработчики, всегда чем-то приходится жертвовать. Точнее искать компромисс между количеством кода, который должен написать пользователь и гибкостью. В этой статье я хочу показать пример такой [...]]]></description>
			<content:encoded><![CDATA[<div id="attachment_1061" class="wp-caption alignnone" style="width: 281px"><img src="http://www.simplecoding.org/wp-content/uploads/2010/05/yii_php_framework_autocomplete.png" alt="yii php framework autocomplete" title="yii php framework autocomplete" width="271" height="166" class="size-full wp-image-1061" style="float:left" /><p class="wp-caption-text"> </p></div>
<p>О том, что фреймворки позволяют сократить время разработки, знают все. Но иногда при этом появляются самые неожиданные ограничения.</p>
<p>По-идее, так и должно быть. Каким бы универсальным не пытались сделать фреймворк разработчики, всегда чем-то приходится жертвовать. Точнее искать компромисс между количеством кода, который должен написать пользователь и гибкостью.</p>
<p>В этой статье я хочу показать пример такой ситуации, и, естественно, её решение.<br />
<span id="more-1073"></span></p>
<p><strong>UPD</strong>: <em>Более удачное решение данной проблемы приведено в комментариях.</em></p>
<p>Не так давно я опубликовал статью <a href="http://www.simplecoding.org/yii-php-frejmvork-sozdaem-pole-s-avtozapolneniem.html">Yii PHP фреймворк: создаем поле с автозаполнением</a>, в которой рассказывал об использовании виджета <a href="http://www.yiiframework.com/doc/api/CAutoComplete">CAutoComplete</a>.</p>
<p><strong>Алгоритм его работы</strong> довольно прост.</p>
<p>1) Создать текстовое поле (тег <code>input</code>).<br />
2) Подключить плагин jQuery <a href="http://plugins.jquery.com/project/autocompletex">Autocomplete</a> и файлы CSS стилей.<br />
3) Подключить плагин к текстовому полю.</p>
<p>При этом, все настройки виджета <code>CAutoComplete</code> задаются при его подключении в <strong>PHP</strong> массиве.</p>
<p>Т.к. основную работу выполняет <strong>Autocomplete</strong>, то логично было бы предположить, что в настройках виджета можно задать все параметры этого плагина. Для большинства параметров, так оно и есть, но с параметром <code>extraParams</code> возникли проблемы.</p>
<p>В документации сказано.</p>
<blockquote><p>Extra parameters for the backend. If you were to specify <code>{bar:4}</code>, the autocompleter would call <code>my_autocomplete_backend.php?q=foo&#038;bar=4</code> (assuming the input box contains &#034;foo&#034;). The param can be a function that is called to calculate the param before each request.</p></blockquote>
<blockquote><p>Дополнительный параметр для бэкенда. Если вы указали <code>{bar:4}</code>, плагин отправит запрос <code>my_autocomplete_backend.php?q=foo&#038;bar=4</code> (предполагается, что текстовое поле содержит текст &#034;foo&#034;). Параметр может быть функцией, которая вызывается чтобы рассчитать значение параметра перед отправкой каждого запроса.</p></blockquote>
<p>Т.е. вы можете задать настройки для <code>CAutoComplete</code> следующим образом.</p>
<pre class="brush: text">$this-&gt;widget('CAutoComplete',
     array(
         'extraParams'=&gt;array('mypar'=&gt;'myval'),
         'url'=&gt;array('countries/autocomplete'),
     )
 );</pre>
<p>И такой вариант будет работать.</p>
<p>Но если вы захотите использовать в качестве значения функцию, например</p>
<pre class="brush: php">'extraParams'=&gt;array('mypar'=&gt;'function() {return $("#myselect").val();}'),</pre>
<p>то этот JS код выполняться не будет.</p>
<p><em>Примечание</em>. Показанная здесь функция возвращает значение из выпадающего списка (<code>myselect</code>).</p>
<p>Дело в том, что функция передается в виде стоки (в кавычках) и JS код при этом, естественно, не выполняется.</p>
<p><strong>Решить проблему</strong> можно так.</p>
<p>1) Создаём js файл с таким кодом.</p>
<pre class="brush: javascript">jQuery("#country").setOptions({
'extraParams':{'mypar':function() {return $("#myselect").val();}}}
);</pre>
<p>2) Подключаем его к нужной странице.</p>
<pre class="brush: php">$cs = Yii::app()->clientScript;
$cs->registerScriptFile(Yii::app()->request->baseUrl.'/js/autocomplete_config.js', CClientScript::POS_END);</pre>
<p>Здесь <code>autocomplete_config.js</code> – название вашего скрипта.</p>
<p>Как видите, решение достаточно простое, но не очень красивое. Часть настроек виджета будет задана в PHP файле, часть в JS.</p>
<p>Если вы знаете более удачные варианты, буду рад выслушать и обсудить <img src='http://www.simplecoding.org/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p>До встречи!</p>
<p><strong>Интересно почитать</strong></p>
<p>Удобный и недорогой <a href="http://www.onyx-inter.ru/stol-i-tumba-kassira-onyx.html ">стол для кассы</a> отечественной разработки</p>
]]></content:encoded>
			<wfw:commentRss>http://www.simplecoding.org/ogranicheniya-frejmvorkov-na-primere-yii.html/feed</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
	</channel>
</rss>

