Страницы

Поиск по вопросам

вторник, 16 октября 2018 г.

Для чего нужен prepare в PDO PHP?

Для чего нужен prepare в PDO PHP? Объясните простым языком новичку.


Ответ

Очень хороший вопрос.
Во-первых, для корректного форматирования данных.
Вот здесь есть хорошее объяснение, но оно по-английски.
Если коротко, то любые данные, которые попадают в запрос, должны быть корректно отформатированы. Иначе они смогут вызвать ошибку или того хуже - уязвимость.
Причем форматировать данные надо надо обязательно перед самым исполнением запроса - не раньше. Поэтому форматированием должен заниматься сам драйвер для работы с БД, в данном случае - ПДО.
Когда мы передаем запрос в prepare(), на место подставляемых данных мы подставляем специальные маркеры. А сами данные передаем после, в execute().
Выполняя запрос, PDO подставляет данные на место маркеров, корректно форматируя их. И таким образом в запросе никогда не будет ни ошибки синтаксиса, вызванной данными, ни - тем более - инъекции.
Поэтому prepare()/execute() необходимо использовать всегда, если в запросе используется хотя бы одна переменная.
Примечание. Способы корректного форматирования данных могут быть разными. В частности, в зависимости от настроек, ПДО может не подставлять отформатированные данные сразу в запрос, а отправлять их отдельно. В этом случае при вызове prepare() запрос вместе с маркерами отправляется в БД, а данные едут отдельно от него, после вызова execute(). В этом варианте данные вообще вообще никак не пересекаются с запросом, а попадают прямиком в БД. Принцип другой, но суть одна - отсутствие ошибок синтаксиса.
Во-вторых, для экономии ресурсов при выполнении одинаковых запросов.
Также при раздельной отправке можно немного сэкономить ресурсы сервера. В этом режиме можно вызвать prepare() всего один раз, а затем только отправлять данные через execute(). Таким образом серверу придется парсить запрос только один раз, что немного сократит общее время выполнения запросов. Но особых чудес повышения производительности от этого способа ждать не стоит.
Подставлять ли данные в запрос сразу или отправлять по отдельности, ПДО решает в зависимости от значения настройки PDO::ATTR_EMULATE_PREPARES

Обработка касаний (струны в гитаре)

Практикуюсь, решил сделать что-то типа гитары, но не придумаю, как правильно обрабатывать нажатия.
Есть 6 струн и надо, когда палец касается верхней струны и идет вниз (зацепляя остальные), чтобы к ним всем, по очереди, применялись анимации.
Смог сделать это только при одиночном касании, если по очереди нажимать на струны то все работает, если прислонить палец о водить по экрану то нет.
@Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { switch (v.getId()) { case R.id.string1: str1.startAnimation(touch_string); return false; case R.id.string2: str2.startAnimation(touch_string); return false; case R.id.string3: str3.startAnimation(touch_string); return false; case R.id.string4: str4.startAnimation(touch_string); return false; case R.id.string5: str5.startAnimation(touch_string); return false; case R.id.string6: str6.startAnimation(touch_string); return false; } } return false; }
Игрался с различными экшенами MotionEvent, но ничего не получилось, как же это правильно реализовать?
Буду благодарен за ответ!


Ответ

Потому что в данном случае нужно отслеживать не ACTION_DOWN, а ACTION_MOVE . Определите нужную область, повесьте слушатель и обработайте как необходимо. К примеру, берете положение и при совпадении в определенных координатах/промежутке координат выполняйте действие
@Override public boolean onTouch(View v, MotionEvent event) { switch (event.getActionMasked()) { case MotionEvent.ACTION_MOVE: x = event.getRawX(); y = event.getRawY(); //TODO обработать нужные места ... break; } return true; }

Использовать чужие картинки из интернета в приложении android? [закрыт]

Много где пишут, что так нельзя. Но ведь не всегда. Можно, например, брать картинки со стоков или любые картинки со свободной лицензией. И тут вроде всё понятно, если ты крадёшь картинку, то можешь рассчитывать только на то, что тебе повезёт и автор на тебя не пожалуется, но. меня привлекла возможность embed'ить картинки, хотя я так и не понял, как конкретно это делать. Просто написать код с интернет-ссылками, по которым программа будет загружать картинки? Для каких картинок этот метод прокатит? Также другой вопрос: если отфотошопить картинку до неузнаваемости, совмещая её с другими, так что все картинки потеряют своё изначальное значение, будет ли это считаться моим творчеством?


Ответ

Это необходимо знать при работе с любым материалом, и графическим в том числе. Я постараюсь кратко описать суть.
Если вы используйте чьи-то работы, вам необходимо учитывать ресурс, с которого вы берете информацию, — у данного ресурса должна быть лицензия по распространению и использованию (чаще мы привыкли видеть способы: платный/бесплатный). Если таковой не наблюдается, при использовании данного материала правообладатель (проще, автор) имеет полное право предъявить свои требования по использованию данного материала. Это значит, что вы незаконно используете его работы.
Второй момент: очень аккуратно (никогда) не используйте фотографии людей, если они не имеют авторского подтверждения, — такие дела быстро заканчиваются не в вашу пользу, и их сейчас много. Причём если автор жалуется на приложение с таким материалом, будьте уверены, что ваше приложение и ваш аккаунт будут заблокированы в ближайшее время.
Третий момент — отредактированное фото. Здесь тонкая нить, но да — это работает. Если вы не просто изменили цвет, а действительно обработали текстуры и наложили кучу эффектов, автору сложно (невозможно) будет доказать, что это его работа.

Blade Cache in Laravel 5+

При обновлении шаблонов Blade приходится долго ждать изменений по причине кеширования. Отключать кеширование в php.ini не вариант. Нашел на просторах решение с прописанем middleware:
namespace App\Http\Middleware;
use Closure; use Illuminate\Contracts\Routing\Middleware;
class ClearCache implements Middleware {
/** * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { $cachedViewsDirectory = app('path.storage').'/framework/views/'; if ($handle = opendir($cachedViewsDirectory)) { while (false !== ($entry = readdir($handle))) { if(strstr($entry, '.')) continue; @unlink($cachedViewsDirectory . $entry); } closedir($handle); } return $next($request); } }
но при загрузке страницы blade говорит что не нашел последний кеш файл и попросту выбивает ошибку. Есть ли вариант уменьшить время или убрать вовсе.


Ответ

Для Laravel 5.0 нужно установить http://packalyst.com/packages/package/kyslik/view-clear В Laravel 5.1+ уже идёт в комплекте.
namespace App\Http\Middleware;
use Closure; use Illuminate\Contracts\Routing\Middleware; use Illuminate\Support\Facades\Artisan;
class ClearCache implements Middleware {
/** * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { Artisan::call('view:clear'); return $next($request); } }

Как правильно “разделять” приложение на слои?

В своих приложениях я использую подход разделение функционала на слои, слои я стараюсь группировать на основании функциональности
Data Access Object Data Access Layer Business Logic Layer GUI
Для работы с бд я использую entity-framework. Довольно часто я стал ловить себя на мысли что я не понимаю/не уверен на каком из слоев должна быть реализована та или иная функциональность
Например: необходимо получить список объектов из бд, пусть класс будет называться Request, тогда я делаю так:
В DAL у меня есть generic репозиторий() c реализацией CRUD и торчащим наружу свойством protected virtual IQueryable Entities{...}
В BLL я обращаюсь к свойству Entities вытягиваю из бд необходимые мне данные, материализую(ToList()Single()), преобразую в DTO объекты которые отдаю в GUI
В целом меня такой подход устраивал всем, базовые методы(CRUD) реализованы в одном классе реализующим generic репозиторий, методы для работы с конкретными сущностями реализованы в слое BLL
Но тут наступил переломный момент: мне понадобилось использовать хранимые процедуры/функции
Для поддержания чистоты приложения доступ к хранимкам хочу(мне так кажется правильней) сделать в слое DAL я это представляю как то так :
Сделать IRequestRepository в который в качестве зависимости передать generic репозиторий плюс реализовать необходимые методы/функции, например такие как обращение и получения результата хранимой функции, так же мне будет необходимо выставить наружу в generic репозитории DbContext что бы можно было обращаться к хранимкам(_context.Database.SqlQuery(...)), дальше же все остается так как и было.
как альтернатива отказаться от generic репозитория и для каждого необходимого класса из DAO реализовать класс репозитория, но в этом случае в каждом классе будет дублирование CRUD методов, что мне кажется не правильным.

Пример реализации:
public interface IRepository where T: class {
T GetById(object id); void Insert(T entity); void Insert(IEnumerable entities); void Update(T entity); void Update(IEnumerable entities); void Delete(T entity); void Delete(IEnumerable entities); IQueryable Table { get; } DbContext Context { get; } }
public interface IRequestRepository { Request GetById(object id);
void Insert(Request request); void Insert(IEnumerable requests); void Update(Request request); void Update(IEnumerable requests); void Delete(Request request); void Delete(IEnumerable requests); }
public class RequestRepository: IRequestRepository { private readonly IRepository _requestRepository;
public RequestRepository(IRepository requestRepository) { this._requestRepository = requestRepository; }
#region Методы обертки над методами из IRepository Request GetById(object id) {...} void Insert(Request request) {...} void Insert(IEnumerable requests) {...} void Update(Request request) {...} void Update(IEnumerable requests) {...} void Delete(Request request) {...} void Delete(IEnumerable requests) {...} #endregion
#region Методы обертки над хранимыми функциями/процедурами IEnumerable GetListOfActiveRequests() { var _requestList = _requestRepository.Context.Database .SqlQuery("Select * from dbo.GetListOfActiveRequests()"); //... } #endregion
//Прочие методы
}
Как все таки лучше/правильней поступить в описанном мной случае?

UPD:
исходя из ответа @PavelMayorov, если я правильно понял структура классов у меня вырисовывается следующим образом
public interface IRequestRepository { #region CRUD методы Request GetById(object id); void Insert(Request request); void Insert(IEnumerable requests); void Update(Request request); void Update(IEnumerable requests); void Delete(Request request); void Delete(IEnumerable requests); #endregion }
public interface IRequestHistoryRepository { #region CRUD методы #endregion }
public class RequestRepository: IRequestRepository { private readonly DbContext _context;
public RequestRepository(DbContext context) { this._context=context; }
// Реализация необходимых методов // для работы с "сущностью" }
public class RequestHistoryRepository: IRequestHistoryRepository { private readonly DbContext _context;
public RequestHistoryRepository(DbContext context) { this._context=context; }
// Реализация необходимых методов // для работы с "сущностью" }
вот только в этом случае будет дублирование в каждом таком репозитории кода CRUD
try { if (request == null) throw new ArgumentNullException("entity");
//Создание/Чтение/Редактирование/Удаление } catch (DbEntityValidationException dbEx) { var msg = string.Empty;
foreach (var validationErrors in dbEx.EntityValidationErrors) foreach (var validationError in validationErrors.ValidationErrors) msg += string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage) + Environment.NewLine;
var fail = new Exception(msg, dbEx); //Debug.WriteLine(fail.Message, fail); throw fail; }


Ответ

Репозиторий со свойством IQueryable Entities - это безусловно лишняя сущность - EF уже сама по себе предоставляет неплохую реализацию репозитория (DbSet). Особенно лишними являются generic-репозитории.
Вообще говоря, в простых программах слоем DAL можно считать саму библиотеку EF; делать поверх новый слой - странное занятие. Если ваша программа достаточно простая - то просто выкиньте из нее все репозитории и увидите как все стало проще :)

Основная проблема слоев DAL в типичных программах - потеря инкапсуляции, схема хранения напрямую связана с внешним интерфейсом из-за использования типов модели хранения в интерфейсах.
Если вам действительно нужен отдельный слой DAL - предлагаю вам выкинуть из его интерфейсов все упоминания моделей EF. Хранение данных должно быть полностью скрыто в слое DAL, никаких классов-сущностей или IQueryable<> наружу торчать не должно. Код уровня бизнес-логики не должен знать, простой ли запрос идет к базе - или же вызывается хранимая процедура.

Что же до повторяющегося кода - есть стандартные способы борьбы с ним: методы-хелперы, базовые классы, AOP.

В чём заключается отличие таких способов взаимодействия приложений, как шлюз и шина?

Читала статью про способы взаимодействия приложений. И что мне осталось совершенно непонятным, это как различаются топологии интеграционного решения "шлюз" и "шина":

Буду благодарна, если кто-нибудь пояснит разницу между ними.


Ответ

В статье "немного" ошиблись с определениями, отсюда и непонятки. Дело в том, что та статья написана, по сути, про одно конкретное решение - и все остальное приведено только для общего сведения.

Интеграционная шина - это прежде всего среда передачи сообщений, позволяющая доставлять сообщения на основе логических признаков. "На пальцах", это означает, что в самих сообщениях не содержатся никакие физические адреса.
Простейшие варианты интеграционных шин держат у себя справочник соответствия логических и физических адресов. Логические адреса формируются исходя из назначения приложения, его названия или оргструктуры. Этого достаточно чтобы называться шиной :)
Более сложные варианты дают возможность доставки сообщений по системе pub/sub (публикатор - подписчик).
Интеграционная шина может быть построена вокруг центрального узла (или нескольких узлов), где "крутится" маршрутизатор (см. далее) - или же быть распределенной, существуя в виде кучи адаптеров (см. далее) на стороне каждого приложения с общей базой адресов.
Маршрутизатор сообщений - это приложение, смысл существования которого - передавать сообщения от одного приложения к другому.
Обычно маршрутизатор сообщений можно найти внутри интеграционный шины - но не каждый маршрутизатор образует вокруг себя шину. Как минимум, нужна работа с логическими адресами.
Также маршрутизаторы сообщений иногда реализуют другие инфраструктурные сервисы, вроде гарантированной доставки, ведения журнала сообщений или преобразования форматов.
Интеграционный шлюз - это маршрутизатор сообщений, связи которого можно поделить на "внешние" и "внутренние".
Эти связи могут работать по одному и тому же протоколу - в таком случае смысл шлюза в разбиении общей шины на сегменты с целью добавления отказоустойчивости или защищенности. К примеру, при наличии филиалов в разных городах, в каждый можно "поставить" шлюз, который будет передавать сообщения в головной офис по шифрованному каналу.
В таком случае общая интеграционная шина окажется разбита на сегменты, которые могут продолжать ограниченно функционировать при падении интернет-канала.
Кроме того, интеграционный шлюз может соединять приложения, работающие по разным протоколам - в таком случае он будет еще и адаптером.
Адаптер - это маршрутизатор сообщений, разные связи которого работают по разным протоколам.
Брокер - это маршрутизатор сообщений повышенной крутизны сложности. К примеру, он может работать с децентрализованной шиной. Или выполнять хитрые трансформации, выступая универсальным центральным адаптером.

Как можно заметить, ни один терминов не исключает другие. Вполне можно себе представить счастье архитектора-перфекциониста в виде мега-интеграционной шины, построенной на маршрутизаторе сообщений, который является брокером, шлюзом и адаптером. Он будет уметь работать по любому протоколу, делать SQL-запросы, проводить XSLT-трансформации - и станет кошмарным сном любого разработчика :)
Однажды я написал свой интеграционный шлюз просто потому что КРОК 2 месяца отказывался предоставлять нашей подсистеме второй адрес на своей интеграционной шине...
А отсюда есть еще один вывод: любую достаточно сложную программу можно обозвать любым словом. Поэтому, чтобы коллеги вас понимали - старайтесь называть ее теми словами, которые используются в официальном названии программы.
К примеру, WebSphere Message Broker имеет в своем названии слово "Брокер" (и заслуженно носит его за свою непомерную сложность крутизну) - поэтому и называть его надо брокером, а не маршрутизатором сообщений, адаптером, шлюзом или шиной.
С другой сторону, если на основе WebSphere Message Broker был сделан свой проект по работе со СМЭВ, который был назван "шлюз для работы со СМЭВ" - то и называть его надо шлюзом, несмотря на то, что он является еще и адаптером и маршрутизатором сообщений.
Или, к примеру, сама СМЭВ по сути является простейшей интеграционной шиной, а внутрях крутится на WebSphere Message Broker (я видел типичные для него сообщения об ошибках) - но называть ее надо СМЭВ, а то не поймут.

Как сделать только один коммит из локальной ветки в remote

Есть удаленный репозиторий
x1->x2->x3 \master
Я сделал его форк и локально новую ветку
/master x1->x2->x3 \y1->y2->y3 \local-develop
Как мне теперь сделать push-request с последним коммитом так, что бы не были видно всей истории коммитов?
/master x1->x2->x3->y3 \y1->y2->y3 \local-develop
Цель в том, что бы добавить результат работы в удаленный репозиторий в отдельную ветку, при этом не захламляя удаленный репозиторий лишними кометами с историей разработки.
Я пробовал сделать так
git checkout local-develop git rebase -i master *squash all commit*
Но получается что они сбиваются в одни в local-develop ветке.
Заранее спасибо.


Ответ

Это так не работает.
Проблема в том, что коммит, семантически, это набор изменений, а не состояние.
Поэтому чтобы отправить последнее состояние, нужно отправить набор изменений от начального (x3, не включительно) до конечного (y3).
И раз вы хотите представить этот набор одним коммитом, то вам придётся сделать один коммит, содержащий в себе изменения y1..y3. С помощью rebase -i вы это и сделали.
Если при этом вы не хотите терять исходные коммиты, можно "сплющивание" (squash) сделать в отдельной ветке (просто перед началом ребейза сделав git checkout -b local-develop-squashed, находясь в ветке local-develop).
После ребейза получится такое состояние:
x1->x2->x3-\ {master} | |->y1->y2->y3 {local-develop} | \->z1 {local-develop-squashed}
В ветке local-develop-squashed по сравнению с master выходит один красивый коммит. Из этой ветки и нужно делать pull request.
У этого, конечно, есть проблемы. Если вы захотите этот pull request доделать, вам придётся делать push --force каждый раз, что в общем случае очень опасно. Поэтому "на посмотреть" можно загрузить и исходную историю из кучи коммитов, и только когда дадут "добро", сделать из кучи коммитов один "прямо на месте" и сделать один раз push --force
А ещё некоторые пользуются при мердже PR опцией --no-ff, а историю смотрят с помощью git log --first-parent master, в результате чего детальная история фича-веток не показывается, а PR выглядят, как отдельные коммиты. И для этого не нужно никакое шаманство с перезаписью коммитов и пушем "силой".