Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
Да, PHP по натуре язык шаблонов, но он не стал слишком хорошим. Впрочем, ничего страшного. Laravel предлагает свой движок Blade, чтобы восполнить пробел. Просто назовите ваши шаблоны с расширением .blade.php, и они соответствующим образом будут парситься. Теперь мы можем делать следующее:
@if ($orders->count()) <ul> @foreach($orders as $order) <li>{{ $order->title }}</li> @endforeach </ul> @endif
<?php if ($orders->count()): ?>
<ul>
<?php foreach($orders as $order): ?>
<li><?= $order->title ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<ul>
{% for order in orders %}
<li>{{order.title}}</li>
{% else %}
<li class="no-records">No records</li>
{% endfor %}
<ul>
<ul>
<?php if ($orders->count()): ?>
<?php foreach($orders as $order): ?>
<li><?= htmlentities($order->title) ?></li>
<?php endforeach; ?>
<?php else: ?>
<li class="no-records">No records</li>
<?php endif; ?>
</ul>
{{ }} можно использовать php.{{{ }}} и данные будут экранироваться.{{{ $name }}}, но если не хотите, чтобы срабатывало экранирование пишите {{ $name }}если заведомо известно, что безопасное экранирование побьёт вывод?
< & "Часто вы генерите JS в шаблоне? Если да — у вас ошибка в архитектуре.
Для оного достаточно экранировать всего три символа, если совсем лень
< & "
Многие CMS позволяют вставлять JS в материалы/шаблоны в админке, что вы видите в этом плохого?
Во-первых, HTML документация не обязывает нас использовать именно кавычки.
Появляются еще и апострофы, которые могут быть и частью аттрибута.
Во-вторых, аттрибуты событий, аттрибут style.
В-четвертых, a href опять же как бы не вышло двойное экранирование амперсанда (urlencode и html).
<a href="?a&b">
<!DOCTYPE html>
<html><body>
<a href="a.html?b&c">
</body></html>
& did not start a character reference. (& probably should have been escaped as &.)
<li><?=_e($order->title) ?></li>
function _e($content ='') {
... эскейпинг, транслейт и все все все.
}
function a($vars)
{
foreach($vars as $key => &$value)
{
$value = ЛЮБОЙ_ЭСКЕЙПИНГ($value);
}
extract $vars;
unset($vars);
require 'template.phtml';
}
$vars = get_defined_vars();
foreach($vars as $key => $value)
{
$$key = ЛЮБОЙ_ЭСКЕЙПИНГ($value);
}
class Templater
{
public function render($file, $vars)
{
$vars = $this->evalVars($vars);
extract($vars);
unset($vars);
require $file;
}
protected function evalVars($vars)
{
$export_vars = array();
foreach($vars as $key => $var)
{
switch(gettype($var))
{
case 'object':
$export_vars = array_merge($export_vars, array( $key => $this->evalVars(get_object_vars($var))));
break;
case 'array':
$export_vars = array_merge($export_vars, array( $key => $this->evalVars($var) ));
break;
default:
$export_vars = array_merge($export_vars, array($key => $this->prepareVar($var) ));
}
}
return $export_vars;
}
protected function prepareVar($var)
{
return $var . 'ЧТО_УГОДНО';
}
}
$templater = new Templater;
$order = (object)(array('id' => 1, 'amount' => 5));
$vars = array('order'=>$order);
$templater->render('template.phtml',$vars);
class TemplateValue
{
private $_value;
public function __construct($value)
{
$this->_value = $value;
}
public function __toString()
{
return $this->escapeValue($this->_value);
}
public function getValue()
{
return $this->_value;
}
public function escapeValue($value)
{
return stripslashes(htmlentities($value));
}
}
function NOTESCAPE($var)
{
return $var->getValue();
}
$vars =array('a'=> '<div>tratata</div>');
foreach($vars as &$var)
{
$var = new TemplateValue($var);
}
extract($vars);
var_dump((string)$a);
var_dump(NOTESCAPE($a));
Ну, я писал выше — в современных объектах далеко не все свойства доступны через public переменные, которые можно вытянуть через get_object_vars().
Всякий lazy loading, getters и прочая магия сильно затрудняют такой прямолинейный подход.
К тому же, возвращать данные может не только свойство, но и метод.
class Order
{
private $_fields=array();
public function setName($value)
{
$this->_fields['name'] = $value;
}
public function __get($name)
{
if (!isset($this->_fields[$name]))
{
throw new \Exception('Invalid field ' . $name);
}
return $this->_fields[$name];
}
}
trait TemplateVarPreparing
{
protected function prepareVar($var)
{
return $var . 'ЧТО_УГОДНО';
}
}
class TemplateProxyObject
{
use TemplateVarPreparing;
protected $_object;
public function __construct($object)
{
$this->_object = $object;
}
public function __call($name, $args)
{
return $this->prepareVar( call_user_func_array($this->_object->$name,$args) );
}
public function __get($name)
{
return $this->prepareVar($this->_object->$name);
}
}
class Templater
{
use TemplateVarPreparing;
public function render($file, $vars)
{
$vars = $this->evalVars($vars);
extract($vars);
unset($vars);
var_dump($order->name);
//require $file;
}
protected function evalVars($vars)
{
$export_vars = array();
foreach($vars as $key => $var)
{
switch(gettype($var))
{
case 'object':
$export_vars = array_merge($export_vars, array( $key => new TemplateProxyObject($var) ));
break;
case 'array':
$export_vars = array_merge($export_vars, array( $key => $this->evalVars($var) ));
break;
default:
$export_vars = array_merge($export_vars, array($key => $this->prepareVar($var) ));
}
}
return $export_vars;
}
}
$templater = new Templater;
$order = new Order;
$order->setName('Katya');
$vars = array('order'=>$order);
$templater->render('template.phtml',$vars);
class ProxyObject
{
protected $_object;
public function __construct($object)
{
$this->_object = $object;
}
public function getObject()
{
return $this->_object;
}
}
Interface IModel
{
}
class Order implements IModel
{
}
$order = new Order;
$proxy_object = new ProxyObject($order);
var_dump(($proxy_object->getObject() instanceof IModel));
function ВЫЗОВХЕЛПЕРА($proxy_object)
{
СТОРОННИЙ_ХЕЛПЕР($proxy_object->getObject());
}
<div>....<?=ВЫЗОВХЕЛПЕРА($order)?>..</div>
Было бы действительно круто, если бы php сделали возможность регить что-то вроде output_filter, как кто-то из хабровчан выше заметил.
<a href="<?= $page->url ?>">...</a>
От такого обилия вложенных угловых скобок глаза начинает колбасить.
Плюс чистый PHP не позволяет удобно использовать всякие полезные вещи, типа наследования шаблонов, автоматического экранирования и т.п.
Laravel Тэйлора Отвелла возник из пепла, чтобы стать любимцем обществаНе тянет статья на Tips and Tricks, скорее очередная пиар статья с кратким описанием фич.
На Стаковерфлоу, скажем, бешеный трафик поддерживается исключительно за счет бангалорских бедняков, для которых писать на пхп — это альтернатива ручной разборке океанских лайнеров на металлолом.
Взрывной рост интереса к ПХП начался в начале века, когда он занял абсолютно пустую нишу
красота кода
Ну и толку от этого контейнера, если весь фреймворк в статике написан.
На серьезном проекте с кучей бизнес-логики без геттеров-сеттеров далеко не уедешь. Чем этот Eloquent лучше, чем какой-нибудь бородатый Kohana ORM? Синтаксис один в один, как и все проблемы.
Верстальщики за такое на кол посадят.
2014 год, а все руками миграции пишем. ОК.
Никто не мешает усложнить логику сеттеров/геттеров. Можно например написать getTitleAttribute(), который будет вызван при попытке доступа к $model->title;
В isFillable мы проверяем несколько разных способов, которыми может быть обработано массовое назначение. Сначала мы смотрим, установлена ли модель как unguarded, и если это так, то разрешаем назначение. Далее мы проверяем, находится ли атрибут в массиве fillable, тогда мы разрешаем назначение еще раз. Затем вызывается isGuarded, который проверяет, находится ли ключ в массиве guarded, или guarded содержит знак звездочки array('*') (который ставится по умолчанию), тогда мы не разрешаем массовое назначение. Наконец, если fillable пустой, мы возвращаем true, если ключ не начинается с подчеркивания, которое используется для скрытых атрибутов, как это принято и в Rails.. Все это сильно затрудняет поддержку проекта.
Если находится unfillable-ключ, то мы проверяем, является ли модель totallyGuarded. По сути это значит, что если fillable пустой и guarded содержит знак * (по умолчанию), тогда возникает исключение MassAssignmentException. Это значит, что если вы не измените свойства fillable или guarded, то при попытке вызвать fill будет возникать исключение MassAssignmentExceptions.
Вообще-то PHP-дэвелопер пишет блэйдовский шаблон ПОСЛЕ того, как его сверстали верстальщики.
… который, конечно, больше никогда не меняется. Такой дзэн.
>> {{ Form::open() }}
>> {{ Form::text('name') }}
>> {{ Form::textarea('bio') }}
>> {{ Form::selectYear('dob', date('Y') — 80, date('Y')) }}
>> {{ Form::close() }}
Верстальщики за такое на кол посадят.
{{ Form::model($user) }}
{{ Form::text('nickname') }}
{{ Form::email('email') }}
{{ Form::close() }}
$form = array('nickname' => 'text',....);
$view_form = Form::render($form);
TextBox textbox1 = new TextBox();
textbox1.width=100;
textbox1.backgroundColor = 165452463;
form1.controls.add(textbox1);
{{ Form::model($user) }}
{{ Form::text('nickname') }}
{{ Form::email('email') }}
{{ Form::close() }}
{{ Form::select('name', $items) }}
$formBuiler->add('category');
$form = new GoodsFormType();
if ('POST' === $request->getMethod()) {
// я давно с ним не работал, так что уже точно не помню API
// могут быть не точности
if ($form->bindRequest($request)->isValid()) {
// форма валидна, можем делать что-то с данными
// данные уже будут в форме уже готовых к использованию объектов
// в нашем случае getData вернет энтити Goods
$data = $form->getData();
// ...
}
// предположим что у нашего экшена уже стоит аннотация что делать с данными
// а именно отрендрить темплейт
return ['form' => $form->getView()];
}
<form action="#" method="post" {{ form_enctype(form) }}>
{{ form_widget(form}}
<input type="submit" />
</form>
<form action="#" method="post" {{ form_enctype(form) }}>
{{ form_widget(form.name, {'attr': {'class': 'foo'}}) }}
{{ form_widget(form.bio) }}
{{ form_widget(form.dob) }}
<input type="submit" />
</form>
if (!Cache::has('data'))
{
Cache::put('data', Question::all(), 3600);
}
$data = Cache::remember('data', 3600, function() {
return Question::all();
});
Это опять руками надо делать?
Да что вы такое говорите. Нормальный верстальщик должен сам писать шаблон.
В нормальных ORM (Propel и Doctrine)
Сгенерировать getTitle() и setTitle() — прямо великая задача. Вроде бы пустяковая вещь, зато такая полезная.
И что, шаблоны прямо никогда не меняются? Это вы в каком-то зазеркальном мире живете.
Здешние программисты не ленивые, а эффективные, потому что вместо того, чтобы тратить время на бесполезные миграции, они пишут бизнес-логику.
$article->getTitle(), чтобы это было расширяемо.getTitleAttribute($value) и делать со значением что угодноЯ не понял, что вы хотите сказать. Я и парень выше вам хотят сказать, что $article->title — очень-очень плохо, должно быть $article->getTitle(), чтобы это было расширяемо.
PHP, Ruby и Питон никто не заставляет учить, но знать шаблонизатор должен
А это значит, что вы не пользуетесь системами деплоймента, дальше даже обсуждать нечего.
[вангамод]Типов полей конечно же в Eloquent тоже нету, т.е. все время придется руками явно приводить все типы[/вангамод]
Всегда считал, что ORM для того и нужен в том числе, чтобы не заморачиваться с полями из селекта.
Судя по вашему ответу, Eloquent-таки делает вот так SHOW COLUMNS FROM my_table;. Долбить БД однотипными запросами тоже считается хорошей практикой в Laravel?
А причем здесь PHP? Тип поля выставляется для ОРМ.
А это значит, что вы не пользуетесь системами деплоймента, дальше даже обсуждать нечего.
text(string $name, string $value = null, array $options = array())textarea(string $name, string $value = null, array $options = array())Но, собственно, мы несколько отошли от изначальной темы разговора. Вопрос остается открытым: «хотят {{ Form::text('name') }} в стиле Pure (http://purecss.io/), а {{ Form::textarea('bio') }} в стиле horizontal form из bootstrap». Каким образом такое делается?
$questions = Question::remember(60)->get();
Насчет кэша в секундах признаюсь, на этот «косяк» уже несколько раз обращали внимание Тэйлора (автора), в том числе и в нашем русском Laravel-коммьюнити, но он упорно не хочет ничего менять, отклоняя пулл реквесты. Что ж, хозяин — барин.
25 Laravel Tips and Tricks