Русское сообщество разработки на PHP-фреймворке Laravel.
Ты не вошёл. Вход тут.
привет, сори за длительный ответ
что фейлится при таком сетапе?
из документации все шаги выполнил? провайдер зарегистрировал?
в composer.json имеется
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
привет, записал видео на эту тему, должно прояснить https://youtu.be/E7P4wf0hPwM
Ты получаешь ошибку call to undefined method stdClass - ключевое для тебя здесь то, что ты работаешь с stdClass. Твоя же задача получать инстанс класса Model который наследуют все твои модели приложения. получить его можно очень просто, используя Eloquent для построения запросов.
// это даст тебе коллекцию из объектов Bestiary (кстати, а что такое Bestiary?)
$bestiary = Bestiary::whereNull('inherited_id')->get();
// соответсвенно в цикле ты будешь работать с моделью
@foreach ($animals as $animal)
$animal->getUrls(); // будет доступен
@enforeach
и старайся как можно реже прибегать к чистым activeRecord, используй Eloquent
"немного покопавшись, я понял, что модель данных и экземпляр данных имеют общее только связку, но не класс." - это как?
такой метод нужно писать в модели Galereya.
У тебя же есть такая модель? или ты просто накатил таблицу, не создав модель?
твои даск тесты должны наследовать базовый DuskTestCase, который в свою очередь наследует PHPUnitTestCase. и все ассерты от туда доступны в твоем даск тесте. у тебя так? создавал тест командой artisan dusk:make?
После того, как ты так настроил в database.php, в phpunit.xml пропиши <env name="DB_CONNECTION" value="testing"/> и тогда в тестах будет использоваться тестовая база. для сетапа ничего делать не нужно, твой трейт RefreshDatabase будет это делать сам - а именно, накатывать миграции перед каждым тестом.
AssertEquals это базовый ассерт предоставляемый phpunit, по ссылке что у тебя это конкретно даск ассерты, надстроенные уже над phpunit и расширяющие его возможности. ты легко можешь использовать все ассерты из phpunit в своих даск тесах, что очень круто. /vendor/phpunit/phpunit/src/Framework/Assert.php - здесь можно посмотреть все ассерты phpunit, ну или в их документации
Данное условие истинно - есть ассерт $this->assertTrue($value). или $this->assertNotNull($value). соответственно есть и обратные, $this->assertFalse and $this->assertNull()
привет!
репозитории нужно использовать тогда, когда очень хорошо понимаешь зачем ты их используешь. этот паттерн в частности используются в высоконагруженных проектах, и позволяет свапнуть реализацию. например если ты хочешь иметь возможность легко переключаться между Eloquent и, не знаю, файловой системой для выборки данных. тогда ты абстрагируешь логики, необходимые для реализации того и того подхода в свои репозитории, и через интерфейс резолвишь ту, что тебе нужна в данных момент.
Я могу предположить что ничего этого в твоем проекте не намечается => выпиливай нахрен все репозитории и не усложняй себе жизнь. используй Eloquent, тем более что уж он так прекрасен.
// даст тебе выборку юзеров по условию в связной таблице
User::whereHas('userInformation', function ($query) {
$query->where('field', 'value');
})->get();
// даст тебе всех юзеров с подгрузкой только той информации, где она удовлятворяет вложенным условиям.
User::with(['userInformation' => function ($query) {
$query->where('field', 'value');
}])->get();
напиши конкретно что хочешь сделать и составим точный запрос
кроном тесты на продакшене? но... зачем? нет, так не делают) зависимости для тестирования даже установлены в require-dev в композере - они по умолчанию на продакш не ставятся. Флоу: ты разрабатываешь фичу, используя тесты. когда твои тесты зеленые, ты можешь быть уверен, что твоя фича работает так как нужно. ты деплоишь код в продакшн. код в продакшене лежит протестированный и работает. и новый код туда не попадает, покуда твои тесты вновь зеленые - и так по кругу. так это работает. По вопросу конкретно, я не в курсе как можно получить отчет о результатах тестирования. Но блин! даже сама идея противоречивая))) то есть, с таким подходом получается, что ты допускаешь, что в продакшене будут фейлиться тесты! но так нельзя, код не может попасть в продакшн, если тесты фейлятся.
1 - нет, это не излишне, ни в коем случае. это делает твою жизнь проще, а в этом и суть тестов. когда у тебя в базе полно данных, ты не можешь наверняка составить ассерты
2) - но это три разных действия, должно обрабатываться трямя разными эндпоинтами, должны иметь три (как минимум своих теста)
3) тестирование на http уровне дает тебе намного более детальное представление о том что происходит, где что фейлится, где рушится логика, ты видишь как легко продвигаешься по стеку исключений от ошибки к ошибке. даск просто дает финальный фейл (как в твоем примере) и не понятно на каком этапе что сломалось. погляди мои видео https://www.youtube.com/watch?v=pUV_tVQlsBE - я сейчас пишу бэкенд для апи для своего приложения, делюсь тем что есть. круто что ты пытаешься внедрить тесты в свое воркфлоу, это правильный путь)
Вьюхи тоже можно нужно тестировать http уровне. допустим ты тестируешь листинг товаров. в фазе сетапа теста ты создаешь несколько товаров, (при этом создаешь их с такими параметрами, как нужно тебе, что опять же невозможно работая с нечистой базой). в фазе экшена делаешь гет запрос на эндпоинт. проверяешь что получил ответ такой то, урл у тебя такой то, проверяешь что вьюха получила необходимый набор из созданных тобою товаров. $this->assertViewHas. и все. а тестировать местоположение элементов на страницы - ну это такое. это значит что если ты решил изменить слегка дизайн, или например изменить формат даты во вьюхе, или используешь другой css фреймвор и классы твоих элементов поменялись - твои даск тесты упадут. будет ли это означать что твое приложение перестало работать? нет. ты просто поменял дизайн. для чего тогда тесты, если они не тестируют поведение, а ломаются от смены дизайна.
Бомбануло чот))
Дружище, привет.
по вопросу валидации - ты можешь легко вынести ее в FormRequest, так как все Get параметры точно так же доступны в Request как и Post параметры. То есть ты можешь делать $request->shop, $request->category. ну или $this->category, $this->shop если уже в FormRequest классе.
Это раз.
По поводу рефакторинга. очень хороший вопрос и проблема весьма и весьма распространенная. когда контроллер превращается в вермишель изза большого кол-ва проверок на входящие параметры. я не смогу тебе щас тут это расписать, ибо займет много времени и места. лучше погляди мои видео, где я пишу бэкенд для апи для своего стартапа (ссылка в подписи), я там уделяю большое внимание рефакторингу и best practices. и я сделаю видео на эту тему, где хорошо ее смогу осветить (спасибо за идею кстати).
А пока можешь зарефачить валидацию, это уже будет что-то. так же валидация в FormRequeste будет возвращать и список ошибок, что тоже существенный плюс.
ну и кстати, если уж и не использовать форм реквест, то используй метод validate() который зашит в сам Request. $request->validate(['shop'=> 'integer'...]); он также позволит тебе не заботиться о Response и об возвращаемых ошибках
Дружище, привет. замечаний будет много
Первое - в тестах нужно использовать чистую отдельную БД и использовать трейт RefreshDatabase. это для того, чтобы А) - не засунуть/удалить по ошибке лишних данных в основную БД, Б) сделать жизнь проще. ларавел позволяет это сделать очень просто. в phpunit.xml, в тэге php добавь следующие записи
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
то есть будешь использовать sqlite базу подгружаемую в память. можно конечно и настроить отдельное MySql соединение в database.php для тестирования. но такой метод проще и в 95% случае именно то, что нужно
Второе, как уже сказал, заюзай трейт RefreshDatabase и убери все DB::beginTransaction rollback и коммит.
Третье - Теперь, когда у тебя чистая база, ты легко можешь сделать утверждения на счет того, что ты ожидаешь по результатам своего теста. Например, я так понимаю, что при сабмите формы кнопкой .editor_button_submit должна создаться новая запись в... DocumentCategory? ок, если так, наверно это у тебя модель? тогда ты можешь сделать следующее:
$this->assertEquals(1, DocumentCategory::count());
все - ты протестировал что у тебя создалась новая запись в DocumentCategory. Ты можешь быть в этом уверен, потому что ты начинал с чистой базы, и у тебя не было ни одной записи ни в DocumentCategory, ни в какой либо другой модели. Так же, ты можешь добавить утверждений
$documentCategory = DocumentCategory::first(); // взяли вновь созданную модель, тк она одна в базе
$this->assertEquals("D", $documentCategory->type); // проверяем, что то, что юзер ввел в форме, то и сохранилось в модель
$this->assertEquals("content lorem...", $documentCategory->content); // чтобы это проверить, избавься от time() - он не нужен тебе
$this->assertRedirect('uri');
ну и напоследок, dusk - не самый оптимальный выбор для тестирования поведения моделей. гораздо лучше тестировать на http уровне, что ларавел тоже делает очень простым и приятным занятием
$user = factory(User::class)->create();
$this->actingAs($user)->post('url куда уходить пост запрос',[
'type' => 'D',
'content' => 'Lorem ipsum...'
]);
Привет! расскажи немного о логике приложения - что пытаешься сделать? звучит как Visit - это счетчик кол-ва посещений Ticket для каждого пользователя (Person)? это так?
привет!
чтобы получать данные из стороннего АПИ хорошим тоном считается созданием, так называемого Gateway, то есть некого шлюза который будет связывать логику твоего приложения и логику стороннего. на простом языке это обычный класс. например
<?php
class SomeServiceGateway
{
public function getItems()
{
// здесь мы общаемся со сторонним апи
// если этот сервис предоставляет либу для работы
// со своим апи используем его здесь.
$result = \SomeOtherSerivce\Items::get();
// если нет - просто делаем запросы руками по их документации
$response = \GuzzleHttp\Client::get('url', 'params');
return $this->format($resposne);
}
protected function format($response)
{
// каким то образом приводим ответ от стороннего сервиса в тот вид,
// который будет удобен для нашего приложения и подходит под его нужды
// в этом и весь смысл Gateways.
return json_decode($response->getBody()->getContents());
}
}
теперь чтобы поделиться этим всем во своими вьюхами можно использовать view composer https://laravel.com/docs/5.6/views#view-composers
Или можно использовать View::share,
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
View::share('items', (new SomeServiceGateway)->getItems());
}
Дефолтные контроллеры приведены просто как пример. опять же, если у тебя апи для стороннего приложения, ты скорее всего захочешь отдавать ответ отличный от того, что предлагают дефолтные контроллеры. в этом случае и логика их поменяется, например, при регистрации тебе нужно будет генерить токен для нового юзера и отдавать его в ответе, чтобы твое мобильное приложение использовало его в последующих запросах.
если же у тебя просто веб приложение, легко можешь использовать дефолтные контроллеры, например переопредлив в контроллере метод из трейта
protected function authenticated(Request $request, $user)
{
// вместо редиректа по дефолту вернем какой-то json response
return response()->json(['status' => 'OK']);
}
Далее, по поводу именования роутов
тут многое зависит от того, что за приложение ты строишь. если это будет апи например для мобильного приложения, то имеет огромный смысл все роуты писать в routes/api.php. по причинам описанным тобою выше — токен авторизация из коробки.
Если это просто веб приложение, на которое ты хочешь натянуть vue на фронтенде, можно обойтись вообще без routes/api и писать все в web.php. и просто отдавать ответы в виде json ответов. в этом случае не подразумевается аутентификация по токенам, и соответственно паспорт не нужен, аутентификация будет происходить по сессии, и слой усложнений в виде паспорта и поддержки аутентификации по токенам здесь излишний
если ты хочешь префиксовать роуты на которые vue будет делать запросы как /api но у тебя не чистый API для стороннего приложения, никаких проблем, объявим роут группу в web.php с префиксом api, и пиши их там.
Дружище, привет. по вопросу 401
если ты используешь пасспорт, то убедись что добавил middleware который будет генерить токен при запросах из vue к бэкенду
'web' => [
// Other middleware...
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],
таким образом, тебе не нужно получать токен руками, когда ты используешь свой же API из своего же фронтенда. Ларавел будет это делать за тебя, а токен потребуется только для сторонних приложение (мобильное приложение, которое будет использовать тот же api)
Дружище, удалять ресурс GET запросом не самая хорошая идея. ознакомся с REST архитектурой
Если вкратце, для твоего случая тебе нужен запрос типа DELETE
<form method="post" action="/articles/{{ $article->id }}">
{{ csrf_field() }} // это предотвратит несанкционированный доступ к твоему эндпоинту со сторонних приложений, чего ты лишаешься при GEt запросе
{{ method_field('DELETE') }}
</form>
//в routes/web
Route::delete('articles/{article}', 'ArticlesController@destroy')->name('articles.destroy');
// В контроллере
public function destroy(Article $article, Request $reqeust)
{
// авторизация на удаление в Reqeust
...
$article->delete();
}
C 5.3 обновляться конечно уже нужно. с версии 5.3 прошло уже больше года, а может и полутора. добавлено огромное кол-во новых фич, особенно в части написания тестов, если используешь тесты то обновиться нужно обязательно. Да и вообще, не вижу никаких проблем обновляться через недельку другую после релиза новой версии. laravel это продукт того качества, когда можно не переживать за то, что [I]вдруг что-то сломается[/I]. Плюс, чем быстрее ты это делаешь, тем безболезненнее процесс.
если просто ответить на твой вопрос — то наверно создать в public/js/app.js и туда писать js код)
в шаблоне сделать <script src="/js/app.js"></script>
Но если тебе хочется обойти сборку webpackом стороной, то скорее всего ты что-то упускаешь из виду. такие инструменты сделаны наоборот для того, чтобы сделать твою жизнь лучше)
Butirator рад что смог помочь
я пишу API для своего мобильного приложения, делюсь с миром через видео уроки https://www.youtube.com/watch?v=pUV_tVQlsBE&t=1s
Заходи, смотри, поддержи проект)
дружище, тебе надо обернуть эту операцию в Job.
php artisan make:job RewardUser
// RewardUser
public function handle()
{
$this->user->money = ( $this->site->double_rewards ) ? ( $this->site->reward_amount * 2 ) : $this->site->reward_amount + $this->user->money;
$this->user->save();
}
// в контроллере
...
$job = (new RewardUser($user, $site))->delay(60*5);
dispatch($job);
из дополнений не по теме, но по best practices: оберни операцию награждения юзера в читаемый метод в модели юзера
// App\User
public function reward($site)
{
$this->money = $site->double_rewards
? ( $site->reward_amount * 2 )
: $site->reward_amount + $this->money;
$this->save();
// тогда в Джобе в handle вызовешь
$user->reward($this->site); // красиво и понятно
}
а еще мне кажется у тебя в логике ошибка
когда $this->site->double_rewards возвращает true, ты тупо перезаписываешь поле money для юзера каждый раз, не прибавляя к нему текущее значение этого поля. но может так и нужно
А еще)
имей ввиду, что когда ты выполнишь этот код, он все равно будет выполняться без задержки, потому что у тебя queue_driver = sync. сконфигурируй какой-нить другой, самое простое - database
Company::create($request->all())
->companyInformation()
->create($request->all());
Не знаю, что означает лучше. Если запись в одну строчку улучшает, то можно так.
Тебе нужно еще таблица converstion_user
User::belongsToMany(Conversation)
User::hasMany(Message)
Conversation::belongsToMany(User)
Conversation::hasMany(Message)
Message::belongsTo(User) // в сообщение обязательно должно быть user_id иначе как мы узнаем кто его отправил. у тебя его нет. так же сюда можно засунуть read_at чтобы отображать прочитано оно или нет
Message::belongsTo(Conversation)
связку user->conversation можно еще объявить как
// User
public function conversations()
{
return $this->belongsToMany(Conversation::class)
->withPivot('name') // сюда можно положить название которое будет отображаться для каждого участника диалога. например если общаются Василий с Петром то для Василия диалог будет отображатьс как "Петр", а для Петра как "Василий"
->withTimestamps();
}
Я думаю они ошиблись в наборе имеил/пароля...)
А разве Работа != OrderService? Если нет, то в чем различие?
из того что видно, поля в ней такие же как в order_service? Под нее есть модель сейчас, Work? что она делает?
Эти строки на скриншоте - это order_services или уже works?
Может отличие в том, что order_service это список доступных сервисов, а работа - непосредственно выполненная работа по этим сервисам? тогда можно проставить order_services.is_complete и вытаскивать потом те, которые is_complete чтоб получить Работы