Viewed   76 times

Some friends and I decided to start working on a project and we came across Laravel and thought it might be a good tool. We started using it locally to develop out some of our pages and noticed something strange.

When we update a view with different information, it would take almost 5 to 10 minutes before the views information would change. It's like Laravel is caching the view and put a TTL on it.

I know this isn't anything I am doing on my local web server because I have used other frameworks and I have never encountered this issue.

Upon searching the Internet, I can't find a great answer on how to disable this. I want to use Laravel, but find it worthless if it takes a while for my views to update each time I want to make a change. In fact, it sounds counter productive.

Is there any way to disable this? Why are my views taking forever to update right out of the box?

 Answers

4

The #laravel IRC channel is a God send. This had nothing to do with Laravel's behavior at all. This was actually something PHP 5.5 was doing.

The reason this was so baffling is because I upgraded my PHP version from 5.3 and never had this issue.

In your .ini file, you need to tweak your OPcache settings. For me, these settings began at line 1087 in the .ini file and looked something like this:

opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.revalidate_freq=60
opcache.fast_shutdown=1
opcache.enable_cli=1

Take particular note of the opcache.revalidate_freq=60. This is what is actually making your views cache. If this is not the desired behavior, set the value to 0 and your views will update every time you make a change. Yay!

EDIT AUGUST 21, 2014

As mentioned by Matt below, make sure to restart your web server to see your changes take effect after you have changed your .ini file.

Tuesday, September 6, 2022
5

So i was looking for an answer to the same question as OP but was not really satisfied with the solutions. So i started playing around with this recently and going through the source code of the framework, I found out that the remember() method accepts second param called key and for some reason it has not been documented on their site (Or did i miss that?).

Now good thing about this is that, The database builder uses the same cache driver which is configured under app/config/cache.php Or should i say the same cache system that has been documented here - Cache. So if you pass min and key to remember(), you can use the same key to clear the cache using Cache::forget() method and in fact, you can pretty much use all the Cache methods listed on the official site, like Cache::get(), Cache::add(), Cache::put(), etc. But i don't recommend you to use those other methods unless you know what you're doing.

Here's an example for you and others to understand what i mean.

Article::with('comments')->remember(5, 'article_comments')->get();

Now the above query result will be cached and will be associated with the article_comments key which can then be used to clear it anytime (In my case, I do it when i update).

So now if i want to clear that cache regardless of how much time it remembers for. I can just do it by calling Cache::forget('article_comments'); and it should work just as expected.

Hope this helps everyone :)

Wednesday, November 30, 2022
 
mtorres
 
5

Try placing the following in /config/environments/development.rb:

# Temporarily enable caching in development (COMMENT OUT WHEN DONE!)
config.action_controller.perform_caching = true

Additionally, if your cache store configuration is in /config/environments/production.rb, then you will need to copy the appropriate line into development.rb as well. For example, if your cache store is the Dalli memcache gem:

# copied from production.rb into development.rb for caching in development
config.cache_store = :dalli_store, '127.0.0.1' 

Hope that helps.

Tuesday, October 4, 2022
 
1

An alternative method to this would be to check for the 'If-Modified-Since' request header as it will only be present if the browser already has the file.

If it is present, then you know the file is already created and can respond with a link to it, otherwise run your code above. Something like this...

// check if the client validating cache and if it is current
if ( isset( $headers['If-Modified-Since'] ) && ( strtotime( $headers['If-Modified-Since'] ) == filemtime( $image->get_full_path() ) ) ) {

    // cache IS current, respond 304
    header( 'Last-Modified: ' . $image->get_last_modified(), true, 304 );

} else {

    // not cached or client cache is older than server, respond 200 and output

    header( 'Last-Modified: ' . $image->get_last_modified(), true, 200 );
    header( 'Content-Length: ' . $image->get_filesize() );
    header( 'Cache-Control: max-age=' . $image->get_expires() );
    header( 'Expires: '. gmdate('D, d M Y H:i:s GMT', time() + $image->get_expires() ) );
    header( 'Content-Type: image/jpeg');

    print file_get_contents( $image->get_full_path() ); 
}
Sunday, November 20, 2022
 
oglop
 
1

General explanation

When you access a Laravel Blade view, it will generate it to a temporary file so it doesn't have to process the Blade syntax every time you access to a view. These files are stored in app/storage/view with a filename that is the MD5 hash of the file path.

Usually when you change a view, Laravel regenerate these files automatically at the next view access and everything goes on. This is done by comparing the modification times of the generated file and the view's source file through the filemtime() function. Probably in your case there was a problem and the temporary file wasn't regenerated. In this case, you have to delete these files, so they can be regenerated. It doesn't harm anything, because they are autogenerated from your views and can be regenerated anytime. They are only for cache purposes.

Normally, they should be refreshed automatically, but you can delete these files anytime if they get stuck and you have problems like these, but as I said these should be just rare exceptions.

Code break down

All the following codes are from laravel/framerok/src/Illuminate/View/. I added some extra comments to the originals.

Get view

Starting from Engines/CompilerEngine.php we have the main code we need to understand the mechanics.

public function get($path, array $data = array())
{
    // Push the path to the stack of the last compiled templates.
    $this->lastCompiled[] = $path;

    // If this given view has expired, which means it has simply been edited since
    // it was last compiled, we will re-compile the views so we can evaluate a
    // fresh copy of the view. We'll pass the compiler the path of the view.
    if ($this->compiler->isExpired($path))
    {
        $this->compiler->compile($path);
    }

    // Return the MD5 hash of the path concatenated
    // to the app's view storage folder path.
    $compiled = $this->compiler->getCompiledPath($path);

    // Once we have the path to the compiled file, we will evaluate the paths with
    // typical PHP just like any other templates. We also keep a stack of views
    // which have been rendered for right exception messages to be generated.
    $results = $this->evaluatePath($compiled, $data);

    // Remove last compiled path.
    array_pop($this->lastCompiled);

    return $results;
}

Check if regeneration required

This will be done in Compilers/Compiler.php. This is an important function. Depending on the result it will be decided whether the view should be recompiled. If this returns false instead of true that can be a reason for views not being regenerated.

public function isExpired($path)
{
    $compiled = $this->getCompiledPath($path);

    // If the compiled file doesn't exist we will indicate that the view is expired
    // so that it can be re-compiled. Else, we will verify the last modification
    // of the views is less than the modification times of the compiled views.
    if ( ! $this->cachePath || ! $this->files->exists($compiled))
    {
        return true;
    }

    $lastModified = $this->files->lastModified($path);

    return $lastModified >= $this->files->lastModified($compiled);
}

Regenerate view

If the view is expired it will be regenerated. In CompilersBladeCompiler.php we see that the compiler will loop through all Blade keywords and finally give back a string that contains the compiled PHP code. Then it will check if the view storage path is set and save the file there with a filename that is the MD5 hash of the view's filename.

public function compile($path)
{
    $contents = $this->compileString($this->files->get($path));

    if ( ! is_null($this->cachePath))
    {
        $this->files->put($this->getCompiledPath($path), $contents);
    }
}

Evaluate

Finally in Engines/PhpEngine.php the view is evaluated. It imports the data passed to the view with extract() and include the file with the passed path in a try and catch all exceptions with handleViewException() that throws the exception again. There are some output buffering too.

Saturday, December 24, 2022
 
masitko
 
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :