AnrDaemon
Продвинутый новичок
Невозможно отфильтровать по нескольким критериям сразу.Даже интересно, почему убожество то?

Невозможно отфильтровать по нескольким критериям сразу.Даже интересно, почему убожество то?
Учимся читать. Не только ABNF, но вообще в принципе читать.Или ты не согласен с этим утверждением?
pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"Да мне пока без надобности. Это трейт RefreshDatabase все стирал.Есть такое волшебное слово - VCS.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Post;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Routing\Redirector;
use Illuminate\View\Factory as ViewFactory;
class PostController extends Controller
{
    private ViewFactory $viewFactory;
    private Redirector $redirector;
    private Post $post;
    private array $fields = ['title', 'slug', 'excerpt', 'content', 'author', 'category'];
    public function __construct(ViewFactory $viewFactory, Redirector $redirector, Post $post)
    {
        $this->viewFactory = $viewFactory;
        $this->redirector = $redirector;
        $this->post = $post;
    }
    
    public function index(): View
    {
        $posts = $this->post->latest()->paginate(5);
        return $this->viewFactory->make('posts.index', ['posts' => $posts]);
    }
    public function create(): View
    {
        return $this->viewFactory->make('posts.create');
    }
    public function store(Request $request): RedirectResponse
    {
        $request->validate([
            'title' => 'required|string|max:500',
            'slug' => 'required|string|max:50|unique:posts',
            'excerpt' => 'required|string|max:1000',
            'content' => 'required|string',
            'author' => 'required|string|max:50',
            'category' => 'required|string|max:50'
        ]);
        $this->post->create($request->only($this->fields));
        return $this->redirector->route('posts.index')->with('success', 'Пост успешно создан!');
    }
    public function show(string $slug): View
    {
        $post = $this->post->where('slug', $slug)->firstOrFail();
        return $this->viewFactory->make('posts.show', ['post' => $post]);
    }
    public function edit(string $slug): View
    {
        $post = $this->post->where('slug', $slug)->firstOrFail();
        return $this->viewFactory->make('posts.edit', ['post' => $post]);
    }
    public function update(Request $request, string $slug): RedirectResponse
    {
        $request->validate([
            'title' => 'required|string|max:500',
            'slug' => 'required|string|max:50',
            'excerpt' => 'required|string|max:1000',
            'content' => 'required|string',
            'author' => 'required|string|max:50',
            'category' => 'required|string|max:50'
        ]);
        $post = $this->post->where('slug', $slug)->firstOrFail();
        $post->update($request->only($this->fields));
        return $this->redirector->route('posts.index')->with('success', 'Пост успешно обновлён!');
    }
    public function destroy(string $slug): RedirectResponse
    {
        $post = $this->post->where('slug', $slug)->firstOrFail();
        $post->delete();
        return $this->redirector->route('posts.index')->with('success', 'Пост успешно удалён!');
    }
}<?php
use Tests\TestCase;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use App\Models\Post;
class PostControllerTest extends TestCase
{
    use DatabaseTransactions;
    
    public function testIndexMethod()
    {
        Post::factory()->count(10)->create();
        $response = $this->get(route('posts.index'));
        $response->assertStatus(200);
        $response->assertViewIs('posts.index');
        $response->assertViewHas('posts');
        $this->assertCount(5, $response->original->getData()['posts']);
    }
    public function testCreateMethod()
    {
        $response = $this->get('/posts/create');
        $response->assertStatus(200);
        $response->assertViewIs('posts.create');
    }
    public function testStoreMethod()
    {
        $data = Post::factory()->make()->toArray();
        $response = $this->withoutMiddleware()->post(route('posts.store'), $data);
        $this->assertDatabaseHas('posts', [
            'title' => $data['title'],
            'slug' => $data['slug'],
            'excerpt' => $data['excerpt'],
            'content' => $data['content'],
            'author' => $data['author'],
            'category' => $data['category']
        ]);
        $response->assertRedirect(route('posts.index'));
        $response->assertSessionHas('success', 'Пост успешно создан!');
    }
    
    public function testShowMethodDisplaysPost()
    {
        $post = Post::factory()->create(['slug' => 'test-slug']);
        $response = $this->get(route('posts.show', ['slug' => 'test-slug']));
        $response->assertStatus(200);
        $response->assertViewHas('post', $post);
    }
    
    public function testShowMethod404()
    {
        $response = $this->get(route('posts.show', ['slug' => 'bla-bla']));
        $response->assertStatus(404);
    }
    
    public function testEditMethodDisplaysView()
    {
        $post = Post::factory()->create(['slug' => 'test-slug']);
        $response = $this->get(route('posts.edit', ['slug' => 'test-slug']));
        $response->assertStatus(200);
        $response->assertViewHas('post', $post);
    }
   
    public function testEditMethod404()
    {
        $response = $this->get(route('posts.edit', ['slug' => 'bla-bla']));
        $response->assertStatus(404);
    }
    
    public function testUpdateMethod()
    {
        Post::factory()->create(['slug' => 'test-slug']);
        $newData = [
            'title' => 'Test title',
            'slug' => 'test-slug',
            'excerpt' => 'Test excerpt',
            'content' => 'Test content',
            'author' => 'Test author',
            'category' => 'Test category'
        ];
        
        $response = $this->withoutMiddleware()->put(route('posts.update', ['slug' => 'test-slug']), $newData);
        $this->assertDatabaseHas('posts', $newData);
        $response->assertRedirect(route('posts.index'));
        $response->assertSessionHas('success', 'Пост успешно обновлён!');
    }
    
    public function testUpdateMethod404()
    {
        $newData = [
            'title' => 'Test title',
            'slug' => 'test-slug',
            'excerpt' => 'Test excerpt',
            'content' => 'Test content',
            'author' => 'Test author',
            'category' => 'Test category'
        ];
        $response = $this->withoutMiddleware()->put(route('posts.update', ['slug' => 'bla-bla']), $newData);
        $response->assertStatus(404);
    }
   
    public function testDestroyMethod()
    {
        Post::factory()->create(['slug' => 'test-slug']);
        $response = $this->withoutMiddleware()->delete(route('posts.destroy', ['slug' => 'test-slug']));
        $this->assertDatabaseMissing('posts', ['slug' => 'test-slug']);
        $response->assertRedirect(route('posts.index'));
        $response->assertSessionHas('success', 'Пост успешно удалён!');
    }
    
    public function testDestroyMethod404()
    {
        $response = $this->withoutMiddleware()->delete(route('posts.destroy', ['slug' => 'bla-bla']));
        $response->assertStatus(404);
    }
}
Да. Забываю все время про эту возможность.Ты же свежий php используешь?
Да, квери билдеры ни в коем случае нельзя использщовать за пределами слоя стораджа. Ни в контроллерах, ни в хендерах. Эта самая проблемная часть при сапорте проектов, потому что самая хрупкая часть и изменения часто надо вносить пачками.Я правильно понял, что модели лучше не юзать в контроллерах, а создавать репозитории для них с методами и сами репозитории передавать в контроллеры?
Про это вот не понял. Там же Eloquent. Репозитории чтоли отдельные нужны для чтения и записи?Модель чтения и модель записи - это две разных модели.
Янг под моделями понимал совсем другое, весь флоу обработки запроса, чтобы можно было разбить на отдельные сервисы и разворачивать на разных серверах для масштабирования. А вот модельку в пхп понимании в примерах он использовал одну и ту же. Не думаю что новичка стоит грузить read-write моделями, ему не надо в масштабирование и эта теория пока лишняя.Eloquent это ORM. Способ отображения программной сущности в систему хранения. И обратно.
И, да, надо создавать РАЗНЫЕ модели для чтения и для записи данных, если становится понятно, что не получается просто взять и записать модель обратно в БД. В особо извращённых случаях у тебя может быть одна модель чтения - aggregate root, и толпа моделей для записи данных в разные части этой сущности.
Вот так нормально?А как лучше сделать для загрузки изображений в посты и для аватаров юзеров? Получается два практически одинковых метода. Трейт сделать или лучше сразу отдельный класс? И куда поместить?
<?php
namespace App\Services;
use Illuminate\Http\UploadedFile;
class ImageUploader
{
    const AVATARS_FOLDER = 'avatars';
    const POSTS_FOLDER = 'posts';
    public function __construct(protected $disk = 'public')
    {}
    public function upload(UploadedFile $file, string $folder): string
    {
        $originalName = $file->getClientOriginalName();
        $fileName = pathinfo($originalName, PATHINFO_FILENAME);
        $extension = $file->getClientOriginalExtension();
        $time = time();
        $fileNameToStore = "{$folder}/{$fileName}_{$time}.{$extension}";
        $path = $file->storeAs($this->disk, $fileNameToStore);
        return $path;
    }
    public function uploadAvatarImage(UploadedFile $file): string
    {
        return $this->upload($file, ImageUploader::AVATARS_FOLDER);
    }
    public function uploadPostImage(UploadedFile $file): string
    {
        return $this->upload($file, ImageUploader::POSTS_FOLDER);
    }
}public function store(StorePostRequest $request, ImageUploader $imageUploader): RedirectResponse
{
    $validated = $request->safe()->only($this->fields);
    $image = $request->file('image');
    
    if ($image) {
        $validated['image'] = $imageUploader->uploadPostImage($image);
    }
    $this->postRepository->create($validated);
    return $this->redirector->route(PostController::ACTION_INDEX)->with('success', 'Пост успешно создан!');
}Удалять не обязательно.А как делают удаление категорий в админке? У них же там связь со статьями. Получается, если надо удалить категорию, надо удалить все статьи связанные с этой категорией?
