Laravel по-русски
Русское сообщество разработки на PHP-фреймворке Laravel.
Ты не вошёл. Вход тут.
За время разработки куча миграций скапливается. Где то столбец добавили, другой убрали, потом переименовали итд. Понятно что цепь миграций приводит к нужному результату.. Но можно ли их то почистить и заменить несколькими, а то и одной актуальными?
Теперь ясно. Проверять нужно ролями. Но вот поверх этого можно прикрутить, что то типа проверки ip или куки.
Но стандарт для laravel безусловно проверять для админки роли или что то еще именно у user'ов которые в общей таблицы с юзерами сайта? И проблем с безопасностью то что аутентификация общая с сайтом это не вызывает?
Или может как то вторую что ли систему аутентификации для админки делать...
Про middleware понятно. А какие условия туда пишите? Что можно проверять кроме ролей, коли их не хотят?
Насчет ролей лидер проекта против. Считает, что авторизованные на сайте юзеры должны вообще быть к админке никаким боком. Может http авторизацию по старинке сделать? Какие то может быть есть встроенные еще средства ларавель о которых я не знаю? В другой ларавель проект тащить админку нет смысла, ведь классы и миграции теже...
Вроде бы можно юзеру показывать на боевом сервере что то типа одинаковой 500 страницы на случай не пойманного исключения?
А какая практика лучше middleware писать в route и группировать. Или писать в конструкторах контроллера. Ну кроме скажем того что применяется к очень многим страницам например auth
- Зато другие фреймворки вполне работают и в описанном мной сценарии (подцепляются к DOM с сервера и дальше уже работают сами)
- итого имеем бекенд, который генерирует базовый HTML, бекенд, который реализует API
Тут честно не очень понял… Кроме того что два бекенда… В общем то самый классический старый подход бекенд оттадает html в нем встроены скрипты в которых в свою очередь при надобности ajax запросы к беку уже за json, а то и за html’ом. Вы это имеете ввиду?
Про ангулляр ясно понятно… Да и времени осваивать его сейчас нет.
- Покажи пожалуйста код, так не понятно как лучше определить. Или сам подумай к чему относится main. Если это тип подписки, и у тебя есть класс Subscription, тогда в него.
$user->newSubscription(’main’, $args); Просто main это подписка по умолчанию… Опустить этот аргумент в cashier нельзя. Нужна константа… Видимо действительно в класс Subscription хотя я думал, что может такое в конфиге хранят…
Но бывает нужно скажем для страницы подписки сделать класс page_subscription для верстки скажем в хидере и в тоже время установить тег title subscription page также в хидере который общий для всего.
В общем я думаю как сделать оптимальней вот это:
return view(’home.upgrade’, [
’title’ ⇒ ’Home upgrade’,
’pageclass’ ⇒ ’home_upgrade’,
]);
А наоборот в чистый single page весь сайт или раздел сайта без релоадов vue может? Или все таки может, но реально для этого нужен angullar?Добавлю свои пять копеек - лично я люто ненавижу все эти SPA, которые пихают где не попадя (от разных бложиков - привет Google Groups - до интернет-банкингов - привет... да все почти). SPA - это реинкарнация одностраничников на Flash. В 90% случаев достаточно отдавать адекватный HTML с сервера и сверху навешивать лёгкий JS (хоть на голом jQuery, хоть на фреймворках). Это лучше по всем пунктам: работает быстрее (JS-движок всегда однопоточный, в отличии от рендеринга страницы), загружается быстрее (браузер может использовать предзагрузку/предсказание переходов), нет проблем с тем, что URL в строке адреса действительно соответствует URL'у страницы (в случае косяков с history API), 100% user-friendly для поисковиков и для гиков на lynx и т.д. и т.п.Единственное, где, пожалуй, выигрывают SPA - это дублирование шаблонов на сервере и на клиенте. Но надо ли ради этого городить все остальное?
Все это с одной стороны так. А с другой на lynx и совсем отсталый браузеры уже наплевать. Насчет СЕО на сайте где весь функционал внутри и главное что бы подписались тоже как то не важно.
Да сингл пейдж может быть тормознее и не удобнее в целом обычного html с легкими js, но это в случае каталогов, какие то разнородных сайтов, именно что совсем не похожих на флешку... Это так. Но он лучше там где все это мысленно представить можно как одно приложение и где js функционал достаточно сложный... От jquery в таких случаях много говно-кода и глюков. Много с этим парился.
В общем то приоритет не столько максимальная оптимизация и сео-френдли, сколько удобство юзеров и чистый современный код.
В тоже время прямо весь что бы только API отдавал пока планов нет. Например на страницах инфо профиля, управления подписками итд, это все незачем..
В общем думаю оптимально vue? Или уж простой небольшой js по старинке? Ангулляр видимо избыточен и тяжел в изучении для данной задачи
А наоборот в чистый single page весь сайт или раздел сайта без релоадов vue может? Или все таки может, но реально для этого нужен angullar?
- А на то, что письма реально отправляются — пишется другой тест, на функцию отправки письма.
Да я это знаю. Просто assert того что оно внутри функции отправилось.
- т.е. ты мочишь нужный функицонал и сравниваешь аргументы, если аргументы верны — то по идеи все должно работать.
А можешь послать в нужном направление, где пример такого?
Ну, а проверка того, что письмо пришло на почту и корректно его верстка в браузере отображается это уже не unit, а feature тест? И тут уже просто тестируем условно говоря кликая по ссылкам как всегда…
- Советую vuejs, легок в обучении, взял лучшее из реакта и ангуляра
А он хорошо подходит если я уж так всему сайту строго разделять фронт и бек и отдавать только json не буду? А скажем через js будет работать список файлов и действия с ними, аплоадер конечно… Ну а всякие функции регистрации профиля они как сейчас есть с релоадом…
Особенно важно имитировать отправку емэйлов, работу со сторонним API и т.д.
А как лучше в phpunit имитировать отправку e-mail из нотификации внутри тестируемого метода? Что бы не просто проверилось отсылается ли он, а реально он пришел причем не на адресс фейкового юзера, а на мой ![]()
Конфиги, языковые файлы и константы вместо текста в коде
return $article->type === Article::TYPE_NORMAL;А как и где такую константу лучше определить?
В моем случае:
$user->newSubscription('main', $plan->braintree_plan)Еще лучше использовать специализированный пакет для передачи данных из бэкенда во фронтенд.Подскажи подробнее? Например самая простая задача передать в блейд шаблон из контроллера что нужно вывести какой то css класс именно для данной страницы?
Спасибо! Респект читаю.
Всем привет!
Делаю сайт типа файлового хостинга на laravel. Конечно он немного тестовый, впрочем время покажет, с чего то надо начинать.
На настоящий момент готово все, что связано с авторизацией и профилем, плюс подписки через laravel cashier и braintree. Все это обошлось ну практически без js просто надобности даже не было.
Теперь на очереди система аплоада и управления файлами, храниться файлы будут на другом сервере с доступом через API. Видимо в данном случае и нужен js фреймоворк. Вижу два варианта:
1) Ограничить js функционал текущей открытой страницей и каким то небольшим интерактивом. Вроде самого аплоада, всплывающих слоев, подсказок итп. Тут подойдет и нативный js и jquery и возможно react. По этому подходу есть обширный практический опыт.
2) Сделать что то близкое к single page. Те переходы между формами и списками без реолада и со сменой url, более сложный клиентский функционал и интерактив. И тут видимо уже нужен фреймворк в который роуты прописать angullar, или vue - vue router, или еще что то подобное.
А тут опыта нет. Кроме того верно ли я понимаю, что если роуты будут в js фреймворке, то это должно быть так для всего сайта включая профиль и подписки, а не только управления файлами? И laravel тогда должна будет отдавать уже не html, а json ?
Что посоветуете? Желательно, все таки что то не совсем уж монструозное в обучение. Хотя в общем то какое то время освоению уделить можно.
Понятно, что типа users_payments_logs, записывать для каждого юзера каждое действие с его оплатами...
Всем привет!
А как это все лучше делать?
Допустим у меня есть файл который отвечает на webhook которые stripe шлет, я хочу что бы в ответах писалась отладка, что бы в stripe ЛК видеть что мой сервер ответил. И в тоже время, что бы ее было можно быстро отключить. Как вариант писать в лог, но не в laravel.log, а понятное дело в отдельный лог специальный для webhook.
Есть конечно monolog, но я не очень нашел как без танцев с бубном, что бы он писал в отдельный скрипт не для info, debug итд, а заданный мной тип сообщений, скажем webhook итд.
Просто напечатать сообщение можно конечно обычным echo(), но хотелось бы печатать в каком то симпатичном формате заданном, да и что бы одной директивой выключить их... А костыль писать не хочется.
Может какой пакет посоветуете?
Еще вопрос о валидаторе, да той же самой смены мейла.
Все ли норм? И не стоит ли все возможные кастомные валидаторы где то в одном месте накапливать.
Смысл валидатора в том, что бы проверять что юзер ввел свой пароль при попытке менять мейл
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Auth;
class CheckPasswordValidator extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* @return void
*/
public function boot()
{
Validator::extend('checkpassword', function ($attribute, $value, $parameters, $validator) {
$email = Auth::user()->email;
if (Auth::attempt(array('email' => $email, 'password' => $value))){
return true;
}else{
return false;
}
});
}
/**
* Register the application services.
*
* @return void
*/
public function register()
{
//
}
}
$user->verified = true;
$user->save();
Activation::where('token', $token)->delete();Кстати не замечал в примерах, что бы такие вещи оборачивались в транзакцию. Ну то есть когда мы сохранили статус юзера то должны удалять запись с токеном. По отдельности нет смысла. Нужно ли указывать явно транзакцию. Хотя случай конечно не критичный...
у тебя все еще много логики, связанной с данными
Что бы ты вынес в модель?
Если захочешь сделать еще чище, то и вместо Mail::to используй notifications или вынеси отправку емэйла в отдельный класс/метод.
Как раз хотелось об этом спросить.. А как вынести в нотификацию для user, в данном случае, учитываю что посылать то надо, не на $user->email, а не $request->email, то есть на то, что юзер ввел в форму новый мейл...
Контроллер
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use \App\Http\Requests\ChangeEmail;
use App\Contracts\Auth\ChangeEmailContract;
use Illuminate\Support\Facades\Request;
use Illuminate\Support\Facades\Auth;
class ChangeEmailController extends Controller
{
public function showForm(Request $request)
{
return view('home.account.email')->with([
'title' => 'Change email',
'pageclass' => 'home_account_email',
]
);
}
public function saveForm(ChangeEmail $request, ChangeEmailContract $changeEmailService)
{
$user = Auth::user();
$changeEmailService->sendChangeEmailMail($user, $request->get('email'));
return redirect()->route('home')->with('status', "Confirmation change E-mail link send to ".$request->get('email'));
}
public function emailSet($token, ChangeEmailContract $changeEmailService)
{
$email = Request::get('email');
try {
$user = $changeEmailService->setEmail($token, $email);
}
catch (\App\Exceptions\ChangeEmailNotFoundException $e) {
return redirect()->route('home')
->with('status', $e->getMessage());
}
Auth::login($user);
return redirect()->route('home')
->with('status', 'You successfully activated your new email!');
}
}
Реквест
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Auth;
class ChangeEmail extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'email' => 'required|email|unique:users',
'password' => 'required|checkpassword',
];
}
public function messages()
{
return [
'email.required' => 'Please enter an email address',
'email.email' => 'Please enter a valid email address',
'email.unique' => 'This e-mail is already taken. ',
'password.required' => 'Please enter your password',
'password.checkpassword' => 'Your enter wrong password',
];
}
}
Сервис
namespace App\Services\Auth;
use \App\Models\User;
use \App\Contracts\Auth\ChangeEmailContract;
use \App\Exceptions\ChangeEmailNotFoundException;
use Illuminate\Mail\Mailer;
use Illuminate\Mail\Message;
use \App\Models\EmailChange;
use Carbon\Carbon;
class ChangeEmailService implements ChangeEmailContract
{
protected $mailer;
protected $changeEmailRepo;
public function __construct()
{
}
public function sendChangeEmailMail($user, $email)
{
$token = $this->createChangeEmail($user, $email);
\Mail::to($email)->send(
new \App\Mail\ChangeEmail(array(
'email' => $email,
'token' => $token,
))
);
}
public function setEmail($token, $email)
{
$changeEmail = EmailChange::where(array('token' => $token, 'email' => $email))->first();
if ($changeEmail === null) {
throw new ChangeEmailNotFoundException();
}
$user = User::find($changeEmail->user_id);
if (!$user) {
throw new ChangeEmailNotFoundException();
}
$user->email = $email;
$user->save();
EmailChange::where('token', $token)->delete();
return $user;
}
private function createChangeEmail($user, $email)
{
$email_change = EmailChange::where('user_id', $user->id)->first();
$token = $this->getToken();
if (!$email_change) {
return EmailChange::insert([
'user_id' => $user->id,
'token' => $token,
'email' => $email,
'created_at' => new Carbon()
]);
}
return EmailChange::where('user_id', $user->id)->update([
'token' => $token,
'email' => $email,
'created_at' => new Carbon()
]);
}
private function getToken()
{
return hash_hmac('sha256', str_random(40), config('app.key'));
}
}
Провайдер
namespace App\Providers\Auth;
use Illuminate\Support\ServiceProvider;
class ChangeEmailProvider extends ServiceProvider
{
protected $defer = false;
public function register()
{
$this->app->bind('App\Contracts\Auth\ChangeEmailContract', function ($app) {
return new \App\Services\Auth\ChangeEmailService();
});
}
public function provides()
{
return ['\App\Contracts\Auth\ChangeEmailContract'];
}
public function boot()
{
}
}
Алексей! Как видишь я склонился к моделям вместо Repo, ну а сервис и его подключение в провайдер оставил и ради отложенной загрузки и тк это все таки ларавельно. Request тоже заюзал.