GithubHelp home page GithubHelp logo

rememberable's Introduction

Rememberable, Laravel 5 query cache

Total Downloads Latest Stable Version Latest Unstable Version License

Rememberable is an Eloquent trait for Laravel that adds remember() query methods. This makes it super easy to cache your query results for an adjustable amount of time.

// Get a the first user's posts and remember them for a day.
User::first()->remember(now()->addDay())->posts()->get();

// You can also pass the number of seconds if you like (before Laravel 5.8 this will be interpreted as minutes).
User::first()->remember(60 * 60 * 24)->posts()->get();

It works by simply remembering the SQL query that was used and storing the result. If the same query is attempted while the cache is persisted it will be retrieved from the store instead of hitting your database again.

Installation

Install using Composer, just as you would anything else.

composer require watson/rememberable

The easiest way to get started with Eloquent is to create an abstract App\Model which you can extend your application models from. In this base model you can import the rememberable trait which will extend the same caching functionality to any queries you build off your model.

<?php
namespace App;

use Watson\Rememberable\Rememberable;
use Illuminate\Database\Eloquent\Model as Eloquent;

abstract class Model extends Eloquent
{
    use Rememberable;
}

Now, just ensure that your application models from this new App\Model instead of Eloquent.

<?php
namespace App;

class Post extends Model
{
    //
}

Alternatively, you can simply apply the trait to each and every model you wish to use remember() on.

Usage

Using the remember method is super simple. Just pass the number of seconds you want to store the result of that query in the cache for, and whenever the same query is called within that time frame the result will be pulled from the cache, rather than from the database again.

// Remember the number of users for an hour.
$users = User::remember(60 * 60)->count();

Cache tags

If you want to tag certain queries you can add cacheTags('tag_name') to your query. Please notice that cache tags are not supported by all cache drivers.

// Remember the number of users for an hour and tag it with 'user_queries'
User::remember(60 * 60)->cacheTags('user_queries')->count();

Cache prefix

If you want a unique prefix added to the cache key for each of your queries (say, if your cache doesn't support tagging), you can add prefix('prefix') to your query.

// Remember the number of users for an hour and prefix the key with 'users'
User::remember(60 * 60)->prefix('users')->count();

Alternatively, you can add the $rememberCachePrefix property to your model to always use that cache prefix.

Cache driver

If you want to use a custom cache driver (defined in config/cache.php) you can add cacheDriver('cacheDriver') to your query.

// Remember the number of users for an hour using redis as cache driver
User::remember(60 * 60)->cacheDriver('redis')->count();

Alternatively, you can add the $rememberCacheDriver property to your model to always use that cache driver.

Model wide cache tag

You can set a cache tag for all queries of a model by setting the $rememberCacheTag property with an unique string that should be used to tag the queries.

Relationships

Validating works by caching queries on a query-by-query basis. This means that when you perform eager-loading those additional queries will not be cached as well unless explicitly specified. You can do that by using a callback with your eager-loads.

$users = User::where("id", ">", "1")
    ->with(['posts' => function ($q) { $q->remember(60 * 60); }])
    ->remember(60 * 60)
    ->take(5)
    ->get();

Always enable

You can opt-in to cache all queries of a model by setting the $rememberFor property with the number of seconds you want to cache results for. Use this feature with caution as it could lead to unexpected behaviour and stale data in your app if you're not familiar with how it works.

Cache flushing

Based on the architecture of the package it's not possible to delete the cache for a single query. But if you tagged any queries using cache tags, you are able to flush the cache for the tag:

User::flushCache('user_queries');

If you used the $rememberCacheTag property you can use the method without a parameter and the caches for the tag set by $rememberCacheTag are flushed:

User::flushCache();

Skipping cache

If you need to disable cache for a particular query, you can use the dontRemember method:

User::latest()->dontRemember()->get();

rememberable's People

Contributors

alberto-bottarini avatar bancur avatar dwightwatson avatar jasonmccreary avatar jsleetw avatar kovah avatar laravel-shift avatar matthewnessworthy avatar mellievt avatar propaganistas avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rememberable's Issues

I got error

local.ERROR: exception 'ErrorException' with message 'unserialize(): Error at offset 0 of 328 bytes' in vendor\laravel\framework\src\Illuminate\Cache\FileStore.php:78

sha256 hash

Thanks for your script !

Is there a specific reason you chose to use the quite slow sha256 hash to create the cache keys?

There are very nice and a lot faster hashes you could use. Security is not important , since it is is internal.

Cheers,
Paolo

Caching with where condition

Hi. First of all, that for nice package. May be I'm not understanding cache behavior correct, but you help you clarify something. Lets assume you have such query:

$product = (new Product())->with('plans')->whereIn('id', [1,2])->get(); dump($product);

And I'm using traits inside Product models and Plans model with tags.

And all works fine.

Now I have another query which looks like this

$product = (new Product())->with('plans')->whereIn('id', [3,4])->get(); dump($product);

So system also cached this query with same tags.

So, question. When I updates a plan model, how I can tell to flush only specific cached result, instead to flush ALL?

Thanks.

Feature Request: Auto Purge Option

This package would be great if there is an auto-purge option which flushes entire cache of a model if any record is updated, deleted or added?

Thanks

Is it just me or is this a bug?

I've looked at your package and it looks really interesting, with my application our customers will be able to generate graphs based on data from their dataloggers. However when i tried to implement it remember() does not seem to be recognized.

`<?php

namespace App;

use DB;
use App\Dlog;
use App\Group;
use Illuminate\Database\Eloquent\Model;
use Watson\Rememberable\Rememberable;

class Datalogger extends Model
{
protected $connection = 'mysql';
protected $table = 'datalog_gsm_info';
protected $primaryKey = 'GSM_ID';
public $incrementing = false;

use Rememberable;

public function logs()
{
    return $this->hasMany(Dlog::class, 'GSM_ID')->remember(60);
}

public function groups()
{
    return $this->belongsToMany(Group::class, 'datalog_gsmex_info', 'GSM_ID', 'Property_value');
}

// public function zeroFilled()
// {
//  $id = $this->GSM_ID;
//  $length = strlen($id);
//  $a = 6 - $length;
//  for($i = $a; $i > 0; $i--)
//  {
//      $id = '0' . $id;
//  }
//  return $id;
// }

}
`
This bit of code produces this error : http://puu.sh/oO24D/d570502375.png
What am I doing wrong here?

Compatibility with Laravel 5.2.3

Hi !

I think the vendor isn't compatible with the last version of Laravel (at the moment 5.2.3).

Can you help please ?

Thank's a lot !

Have a nice day !

Emmanuel.

Randomly getting error "file_put_contents"

Hi!

I can't understand what's the problem with my project. I randomly get the error

file_put_contents(<path>): failed to open stream: No such file or directory

By randomly I mean that I have many articles with an ID. By visiting /article/ID I get errors on some pages but other pages works fine.

If I try to clear the cache (php artisan clear:cache) some others random articles give me error but the previous one works fine.

Any idea on what could be the problem?

flushCache

A problem with ::flushCache();

This is a code from my controller.
The query is not made.
The cache is not cleared.
If i use \Cache::flush(); then it's ok.


            MaintenancePart::flushCache();

            \DB::enableQueryLog();
            $this->standardMaintenancePartList = MaintenancePart
                ::extended('standard', $this->isShowDataForAllDealers ? false : $this->rcode)
                ->get(array_merge($cols, ['pack.period_km', 'pack.period_month']))
                ->toArray();
            dd(\DB::getQueryLog());

Model:

<?php

namespace App\Models\Maintenance;

use App\Model;

class MaintenancePart extends Model
{
    /**
     * The database table used by the model.
     *
     * @var string
     */
    protected $table = 'maintenance_part_entity';
    protected $guarded = ['id'];

    protected $rememberFor = 14400; 

    public function scopeExtended($query, $maintenancePackageType = false, $rcode = false)
    {
        $query
            ->leftJoin('maintenance_package as pack', 'pack.id', '=', 'maintenance_part_entity.maintenance_package_id');
        if ($rcode) {
            $query->where('rcode', '=', $rcode);
        }
        if ($maintenancePackageType) {
            $query->where('maintenance_package_type', '=', $maintenancePackageType);
        }

        return $query;
    }

Laravel 5.1.28 php 7.0.14

It does not work inside eager loading

Hello, I have this code:

$posts = Post::with(['translations' => function($query){
    $query->where('lang_code', $this->lang);
}])
->with('author')
->with('tags')
->with(['images' => function($query){
    $query->with(['translations' => function($query){
    $query->where('lang_code', $this->lang);
}]);
}])
->remember(120)
->get();

'remember' only works before get() or just after Post::, and it remembers the first query: select * from posts where posts.deleted_at is null, but not the rest (so I get 5 queries instead of 6)

How can I add Remember inside the Eager Loading?
Am I missing something?

Thank you!

Check if query is cached

Is there a way to check if the query is cached?
I want to make a command that caches commonly used queries - but if they're already cached, don't repeat the query - only repeat the query for ones that are not cached.

Call to undefined method

Am I the only one getting this error ?

BadMethodCallException in Builder.php line 2071:
Call to undefined method Watson\Rememberable\Query\Builder::getQuery()

I installed it using the abstract class as mentioned in the docs, but the first time I try to use any model, I got the error above.

Caching on update query

I'm caching a simple SELECT query, after that query I run an UPDATE query. This query is not executed when I use ->remember() on the SELECT query. Why?

Refresh the cache

Hi!

I've got a large system running rememberable-plugin and it works really well.

Would it be possible to make a refresh-command which updates the result in the caching by the cachingtag?
It could be nice!

Feature proposal: forcing the query to be performed

As in title,
sometimes i need to overrule the remember() request - especially in the development process, when I want to test the chache behavior and judge if it is working as expected.

In other words: sometimes and in some of my templates I want to be 100% sure that I get fresh query, not chached.
ANother use is comparing two queries - cached and not-cached.

Thx.

Remove generated cache by rememberable

Sometimes I want to force a refresh of the cache that has been set by rememberable. Something like: Cache::forget('key'); However I don't know the key and if this way is possible at all?

Proposal: retrieve cache key from model instance

Maybe add a getCacheKey() method to the Rememberable trait?

    /**
     * Get the unique cache key from the query builder.
     *
     * @return string
     */
    public function getCacheKey()
    {
        return $this->newBaseQueryBuilder()->getCacheKey();
    }

Granted, a query builder instance would have to be instantiated for this method to return an effective value, but it was helpful in one of my projects.

Example:

$model = (new Model)->find(1);

\Cache::forget($model->getCacheKey());

TRYING TO USE in SLIMEFRAMEWORK and get error: Call to undefined function Watson\Rememberable\Query\app()

TRYING TO USE in SLIMEFRAMEWORK and get error: Call to undefined function Watson\Rememberable\Query\app()

I have Model.php thus:

namespace Ekuma\Model;
use Watson\Rememberable\Rememberable;
use Illuminate\Database\Eloquent\Model as Eloquent;
abstract class Modeli extends Eloquent
{
    use Rememberable;
}

Then I have Cities.php thus:

namespace Ekuma\Model;
class Cities extends Modeli
{
    protected $table = 'cities';

}

in my script I have
echo Cities::remember(60)->count();

// my composer file

   "autoload": {
    "psr-4": {
        "Ekuma\\": "application/"
    }
},

// SLIM ERROR

Type: Error
Message: Call to undefined function Watson\Rememberable\Query\app()
File: /Applications/XAMPP/xamppfiles/htdocs/userr/vendor/watson/rememberable/src/Query/Builder.php
Line: 182

Call to undefined method: remember()

BadMethodCallException in Builder.php line 2025: Call to undefined method Illuminate\Database\Query\Builder::remember()

Am I doing something wrong?

Multiple cache tags?

Is there a way to set multiple cache tags for a query? (Sorry, first time doing something like this.)
If not, is there a way to flush tags using regular expressions? (e.g. flush all tags with "user:1")
Thank you for this BTW! Was looking for remember() and was not disappointed.

Query isn't cached if pluck method is used rather than get

Hello,

Just started using this today, so maybe I'm doing something wrong but it appears that if you use the pluck eloquent method the query is not cached. However, I was able to get the query to be cached by using the get eloquent method and then the pluck collection method.

This query will not be cached

Model::remember(60)->pluck('name');

This query will be cached

Model::remember(60)->get()->pluck('name');

I would expect Model::pluck() (or any method that triggers the execution of a query) to be cached just like Model::get(). Though I imagine there is a good reason if this is intentionally not the case.

Usage with DB facade without models

Hello,

I'm querying some data via DB facade, like this:

\DB::connection('customconnection')->select('SELECT * FROM mytable')

Is it possible to use the plugin here? How can I add the Rememberable trait?

Thank you!

For Laravel 5 eager loading remember is not working

I have below query which is not cache proper using remember, rest will work fine as expected.
$products = $category->products()->with("inventory","tierPrice","images","url","attrs","attrs.values","attrs.values.images","attrs.values.option","attrs.values.option.image")->active()->orderBy($sort_by, $sort_order)->remember(1440)->paginate($limit);

Any help or suggestion are appreciated !!!

Thanks...

Cannot redeclare class App\Model

I tried used this packages in localhost it is working as normal but when I move to my production it has the problem like above. any suggestion code how to use in model without any issue ? thank you

Proposal: New Major Version

Sticking this one in an issue so it's a little easier to keep track of.

There's a few things that would be really nice for this project that would require a new major version, and one of my PRs requires that anyway, so I thought I'd bring up discussion here.

  • Switch from md5 to sha256 for cache keys (#27)
  • Use a (configurable) cache prefix (#28)
  • Add tests for the Query Builder
  • Configure travis-ci

Builder requires $minutes to be int

https://github.com/dwightwatson/rememberable/blob/master/src/Query/Builder.php#L81

In that line, the Builder checks if $minutes < 0
This is not an error BUT:

According to the Laravel documentation, the remember() method requires 3 types for the first argument:

  • int
  • float
  • \DateTime

(See https://laravel.com/api/5.4/Illuminate/Cache/Repository.html#method_remember)

Is it possible to change this line so it checks for a DateTime instance?

What I want to achieve is the following:

Model::remember(Carbon::now()->addSeconds(30))->get();

This is just an example. Basically, in some cases it would be more practicable to have the possibilty to enter the expiration date as a DateTime, not in minutes (for example, if I want to use seconds instead or maybe one month as a DateTime instead of 43200 (minutes)).

Please answer these questions..

Do this package caches the eloquent queries with paging efficiently..

Do this package clears cache on model update..

is there a way to cache all database queries..

is there a way to implement tag to caching queries..

pls reply soon....Thank you..

flushCache - flushing from wrong cache store

Hi,

I noticed that when using flushCache non of the selected tags where flushed from redis. I believe that this is because the flushCache method takes cache store from app config:

Builder.php line 215

$store = app('cache')->getStore();

so if for example default cache in .env file is set to memcached, but you used redis to cache query, eg.

Model::remember(60)->cacheDriver('redis') ....

flushCache will try to flush tags from memcached instead of redis. To fix this line 215 should rather be:

$store = app('cache')->driver($this->cacheDriver);

So it will flush tags from store where query where cached.

Question: Query Builder?

Is any way to get it on query builder not only as db Eloquent?
when i try it on
DB::table('table')->join('table','t1.field','=','t2.field')->where('t1.field',$var)->remember('1440')->get();

i got an error with Builder.php

Doesn't work on L5.1

Using for instance:

$users = User::where("id", ">", "1")->remember(10)->take(5)->get();

Gives the expected result (no errors). But reloading the page, I see that mysql simply fires the 5 queries again and again....

all method can not use

User::remember(10)->all() failed

Call to undefined method App\Model\Rememberable\Query\Builder::all()

Call to undefined method: remember() (individual model)

I am using the Repository pattern and want to test this trait out on just one problematic model for starters.

I have HotelSearchRepository.php which includes chainable methods. The top of the file looks like this:

namespace App\Repositories\Eloquent;

use Watson\Rememberable\Rememberable;
use App\Repositories\Contracts\EventLocationRepositoryInterface;
use App\Repositories\Contracts\HotelSearchRepositoryInterface;

class HotelSearchRepository extends Repository implements HotelSearchRepositoryInterface
{
    use Rememberable;
...

I am chaining methods for my search functionality like so:

    public function search($terms = [])
    {
        $this->terms = $terms;

        return $this
            ->withMinSleepingRooms($this->term('min_sleeping_rooms'))
            ->withMaxSleepingRooms($this->term('max_sleeping_rooms'))
            ->withMinMeetingRooms($this->term('min_meeting_rooms'))
            ->withMinLargestMeetingRoomSqFt($this->term('min_largest_meeting_room_sq_ft'))
            ->withStarsMatching($this->term('stars'))
            ->withBrands($this->term('brands'))
            ->nearLocations($this->term('locations'), $this->term('radius'), $this->term('radius_units'))
            ->withRelatedModels()
            ->complete();
    }

So the method I'm including the remember trait on is here:

    private function withRelatedModels()
    {
        $this->model = $this->model->with(['propertyType' => function ($q) {
            return $q->remember(10);
        }]);

        return $this;
    }

I am only attempting to cache the propertyType query at this point. However, I get the exception: Call to undefined method Illuminate\Database\Query\Builder::remember().

I have tried this as well, to no avail.

    private function withRelatedModels()
    {
        $this->model = $this->model->with(['propertyType' => function ($q) {
            return $q->remember(10);
        }])->remember(10);

        return $this;
    }

What am I doing wrong here?

Support for Laravel Scout

First of all thank you for this awesome package.

I implemented a search functionality into my app with Laravl Scout and the TNTSearch driver. Unfortunately I was not able to make it work due to the fact that Rememberable does not support the search method provided by Laravel Scout.
I get the following error while trying to search a specific item:

Call to undefined method Watson\Rememberable\Query\Builder::search()

Is it possible to implement this into the pakage? Or is there a way to do this on my own?

Thanks fo the help. :)

Is there a better way to cache paginate?

Item::paginate(20)->remember();

The first page query will be select * from items limit 20,
The sencond page query will be select * from items limit 20 offset 20

They are different raw query statement, so they will be cache twice.

Is there a better way to cache pagination?

`paginate()` doeesn't cache the main record

The setup is phpunit & array cache driver. And I've tapped in to the cache using:

$cache = \Cache::store();

The line below:

$paginator = SomeModel::paginate($perPage)->load('relation');

After that, I've found that there are two entries in the cache: the aggregate & relation. The main model (record) is missed.

While traced a bit, I was led to the line, which traced back to query builder (not eloquent builder) getCountForPagination()

public function getCountForPagination($columns = ['*'])
{
    $this->backupFieldsForCount();

    $this->aggregate = ['function' => 'count', 'columns' => $this->clearSelectAliases($columns)];

    $results = $this->get();    // <<<<<< THIS LINE

    $this->aggregate = null;

    $this->restoreFieldsForCount();

    if (isset($this->groups)) {
        return count($results);
    }

    return isset($results[0]) ? (int) array_change_key_case((array) $results[0])['aggregate'] : 0;
}

Then the subsequent call to forPage()->get() will not hit the cache. Because the $cacheMinutes property was set to null in the closure returned by getCacheCallback(). Which ends up failing the non-null test in get().

public function paginate($perPage = 15, $columns = ['*'], $pageName = 'page', $page = null)
{
    $page = $page ?: Paginator::resolveCurrentPage($pageName);

    $total = $this->getCountForPagination($columns);

    $results = $this->forPage($page, $perPage)->get($columns);   // <<<<<<< THIS WON'T HIT CACHE

    return new LengthAwarePaginator($results, $total, $perPage, $page, [
        'path' => Paginator::resolveCurrentPath(),
        'pageName' => $pageName,
    ]);
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.