traffske
traffske

Reputation: 111

Cache in Laravel

I am beginner in Laravel. In my project I use repository pattern and Laravel 7.

I have this controller:

class PageController extends Controller
{

    protected $model;

    /**
     * PageController constructor.
     * @param PageRepositoryInterface $repository
     */
    public function __construct(PageRepositoryInterface $repository)
    {
        $this->model = $repository;
    }


    /**
     * @param Request $request
     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
     */
    public function index(Request $request)
    {
        if ($request->input('query') != "") {
            $pages = $this->model->search($request->input('query'), 'id', 'asc', [],  30);
        } else {
            $pages = $this->model->listWithPaginate('id', 'desc', [],  30);
        }
        return view('admin.pages.list', ['pages' => $pages]);
    }


    /**
     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
     */
    public function create()
    {
        return view('admin.pages.view');
    }


    /**
     * @param PageRequest $request
     * @return \Illuminate\Http\RedirectResponse
     */
    public function store(PageRequest $request)
    {
        if ($request->isMethod('post')) {
            $data = [
                'title' => $request->input('title'),
                'description' => $request->input('description') ?? $request->input('title'),
                'keywords' => $request->input('keywords') ?? $request->input('title'),
                'content' => $request->input('content'),
                'enable' => $request->input('enable') ?? 0,
                'slug' => Str::slug($request->input('title'), '-')
            ];
            $this->model->create($data);
            return redirect()->route('page.index')->with('success', __('messages.record_save_info'));
        }
    }


    /**
     * @param int $id
     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
     */
    public function edit(int $id)
    {
        Cache::forget("page.{$id}");
        return view('admin.pages.view', ['page' => $this->model->findOrFail($id)]);
    }


    /**
     * @param PageRequest $request
     * @param int $id
     * @return \Illuminate\Http\RedirectResponse
     */
    public function update(PageRequest $request, int $id)
    {
        if ($request->isMethod('put')) {
            $data = [
                'title' => $request->input('title'),
                'description' => $request->input('description'),
                'keywords' => $request->input('keywords'),
                'content' => $request->input('content'),
                'enable' => $request->input('enable') ?? 0,
                'slug' => Str::slug($request->input('title'), '-')
            ];
            $this->model->update($data, $id, 'id');
            return redirect()->route('page.index')->with('success', __('messages.record_edit_info'));
        }
    }


    /**
     * @param Request $request
     * @param int $id
     * @return \Illuminate\Http\RedirectResponse
     */
    public function destroy(Request $request, int $id)
    {
        if ($request->isMethod('delete')) {
            $this->model->delete($id);
            return redirect()->route('page.index')->with('success', __('messages.record_remove_info'));
        }
    }
}

and CachingBaseRepository

abstract class CachingBaseRepository implements RepositoryInterface
{
    use ScopeActiveTrait;

    protected $model;
    protected $cacheKey;
    protected $cacheTime;

    public function all()
    {
        return Cache::remember($this->cacheKey.'.all', $this->cacheTime, function () {
            return $this->model->get();
        });
    }

    public function allEnables()
    {
        return Cache::remember($this->cacheKey.'.enables', $this->cacheTime, function () {
            return $this->model->active()->get();
        });
    }

    public function list(string $orderByColumn, string $orderBy = 'desc', array $with = [])
    {
        return Cache::remember($this->cacheKey.'.list', $this->cacheTime, function () use($with, $orderByColumn, $orderBy) {
            return $this->model->with($with)
                ->orderBy($orderByColumn, $orderBy)
                ->get();
        });
    }

    public function listWithPaginate(string $orderByColumn, string $orderBy = 'desc', array $with = [], int $perPage = 10)
    {
        return Cache::remember($this->cacheKey.'.listWithPaginate', $this->cacheTime, function () use($with, $orderByColumn, $orderBy, $perPage) {
            return $this->model->with($with)
                ->orderBy($orderByColumn, $orderBy)
                ->paginate($perPage)->appends(request()->query());
        });
    }

    public function create(array $data): int
    {
        Cache::forget($this->cacheKey);
        Cache::forget($this->cacheKey.'.all');
        Cache::forget($this->cacheKey.'.enables');
        Cache::forget($this->cacheKey.'.list');
        Cache::forget($this->cacheKey.'.listWithPaginate');
        return $this->model->create($data)->id;
    }

    public function update(array $data, int $id, string $attribute = 'id'): void
    {
        $this->model->where($attribute, '=', $id)->update($data);
        Cache::forget($this->cacheKey);
        Cache::forget($this->cacheKey.".{$id}");
        Cache::forget($this->cacheKey.'.all');
        Cache::forget($this->cacheKey.'.enables');
        Cache::forget($this->cacheKey.'.list');
        Cache::forget($this->cacheKey.'.listWithPaginate');
    }

    public function delete(int $id): void
    {
        $this->model->destroy($id);
        Cache::forget($this->cacheKey);
        Cache::forget($this->cacheKey.'.all');
        Cache::forget($this->cacheKey.'.enables');
        Cache::forget($this->cacheKey.'.list');
        Cache::forget($this->cacheKey.'.listWithPaginate');
    }

    public function find(int $id)
    {
        return Cache::remember($this->cacheKey.".{$id}", $this->cacheTime, function () use ($id) {
            return $this->model->find($id);
        });
    }

    public function getModel()
    {
        return Cache::remember($this->cacheKey.".all", $this->cacheTime, function (){
            return $this->model;
        });
    }

    public function findOrFail(int $id)
    {
        return Cache::remember($this->cacheKey.".{$id}", $this->cacheTime, function () use ($id) {
            return $this->model->findOrFail($id);
        });
    }
}

I have problem with paginate. When I go to pagination 2,3,7 or 10 - I always see the same as on page 1.

Is my code optimal? Can duplication be replaced - one function (I have remove in controller - in edit too):

:: Cache forget ($ this-> cacheKey);
         :: Cache forget ($ this-> cacheKey.. 'All');
         :: Cache forget ($ this-> cacheKey. '. Enables');
         :: Cache forget ($ this-> cacheKey.. 'Letter');
         :: Cache forget ($ this-> cacheKey. '. ListWithPaginate');

some one function?

Upvotes: 0

Views: 1269

Answers (1)

katsarov
katsarov

Reputation: 1822

That is because when you call first page it is cached, on second page since the key is not changed the cache assume that you want cached version and returns page 1.

One way will be to update tag depending on input:

public function listWithPaginate(string $orderByColumn, string $orderBy = 'desc', array $with = [], int $perPage = 10)
    {
        $tag = sprintf('%s.listWithPaginate.%s.%s', 
            $this->cacheKey,
            serialize(func_get_args()),
            serialize(request()->query())
        );
        return Cache::remember($tag, $this->cacheTime...;
    }

Another way will be to cache all rows and then paginate them from cache. You will need to make custom collection paginator (google "laravel collection pagination, and will find many tutorials). You may still need to add $with to your cache tag. And for many results isn't a good idea.

Don't try to cache everything. Caching isn't always the fastest way. Better try to optimize your database.

Personally I don't think it is a good idea to cache in repositories.

To flush multiple tags you can use:

Cache::tags('tag1', 'tag2')->flush()

Upvotes: 1

Related Questions