I know you can cache a query by appending ->remember($minutes).
However, this doesn't work when using loading on an existing Eloquent result. This is because load doesn't return a querybuilder but directly executes it.
Say you want to load the friends of a user:
$user=Auth::user();
$user->load('friends'); // How to cache these results ?
How can I cache these ?
You can't using this code on its own. You do have two options, though:
1) Wrap your code in a wrapper somewhere (essentially abstracting away this call), which does a cache check first, and runs these queries if the cached item does not exist or is expired.
2) Extend the Auth library, specifically its EloquentUserProvider class, and modify the query performed by the retrieveById() method.
You can see one method of abstracting methods and caching in Chris Fidao's Implementing Laravel. The accompanying book is handy!
You have to convert User object into QueryBuilder to get access to ->remember() method
Try
User::with('friends')->where('id', Auth::user()->id)->remember()->first();
Related
Does Laravel have any Caching mechanism to cache something just for the duration of the current script/request? Whether that's using a Cache driver like FileCache or DatabaseCache, or just in-memory cache.
For example, I have some data that is quite violate and changes often, and my scripts gets it in multiple places. So I would like to cache it once I got it the first time, but forget it after the execution of the current script has finished (so on next request it fetches it again).
It would be equivalent of me having some global variable $cache or similar, where I could store for example $cache['options']. Is there something like that in Laravel already?
Thank you.
One way is just following the singleton pattern for the class that does the repeated functionality.
You can also just bind an instance of a class to the Service Container and pull in that dependency where you need to use it.
https://laravel.com/docs/5.4/container
Singleton or instance binding would allow your application to share the same instance of a class anywhere during a single execution.
Having a basic example of a REST endpoint which returns a User json object, how would we fill our Model having that we get the data from a Web Service instead of a DB?
My approach is to have a very very basic 'SDK' with independent Classes with my data representation and access data methods, and in every method I make a call to the API with Guzzle to actually perform the action.
The problem is that I'm losing all the power that Eloquent provides.
Is there any recommended approach to do this in Laravel? Non Laravel approachs will do too.
I'd recommend to use a combination of cache and collections.
First you'll collect the web service results to a collection that you will then cache.
Something like this :
$users = Cache::remember('users', $minutes, function() use ($guzzle) {
$apiUsers = $guzzle->get('api/users'); // Get the API response as json
return collect(json_decode($apiUsers));
});
Using collections you'll have access to almost all of the Eloquent features (because it returns Collection as well. So you might do things like $user = $users->where('email', 'you#email.com')->first();
Then if you want to make it more like an "SDK", you can create a package that has classes and methods that makes it easier to get and use your API.
The problem is that I'm losing all the power that Eloquent provides.
That's not the only problem here. Your approach will be quite ineffective if you are planing to call remote API on each page display and the overall performance will greatly suffer from such approach. What you can do instead is to have your API data cached in local DB, so your models can use Eloquent to get them from with all the benefits.
I'm having a strange problem in my application (Yii Framework 1.1.8).
I called a function as follows:
UserDataModel::model()->cache(3600, $dependency)->getAttributes();
After calling this function I called another model and fetched the data.
ProfileModel::model()->findAll();
To my surprise, ProfileModel was also cached. When I remove the first line (UserDataModel), the ProfileModel fetches the uncached data. Since both models are different, why first model is forcing cache for the next model call ?
Is there anything wrong with my implementation ?
Thanks.
Arfeen
I hope I can help you out, as I can see you are not specifying the third parameter in the cache which indicates the number of queries to be cached. My guess is that if the dependency is true, everything from that line on to the bottom will be cached in the cfilecache created totally independent to the model. In fact I have cache which implements dependency on several tables so I can cache more than one query and in the third parameter I tell the cache how many queries will I save
Okay, so here's the deal. I'm working with a custom CMS, and I'd like for the code to be as optimized as possible. I've been reading/watching tuts/etc. like crazy about the repository pattern in general as well as specifically using it with Laravel's Eloquent. There are probably some really dumb questions/thoughts in here, but bear with me. :-) Sometimes there's no easy way to ask about terminology/best practices without looking silly.
As with many things, there are million ways I could "make it work"; my dilemma is essentially a matter of "best practice."
General Scenario/Question
Let's assume I am trying to get a Page for my CMS from the database. From what I can understand the typical way to set up the repository pattern with Eloquent is to have the following files:
Page.php -- the Eloquent Model
PageRepositoryInterface.php -- the "contract" for what should be in Page repo's
EloquentPageRepository.php -- the Page repository that can grab data via Eloquent
Easy enough. So I might use it this way. Assuming I have a getPageById method in EloquentPageRepository.php, I could just do something like this from my controller:
$page = $this->repo->getPageById();
Now my question arises: what type of data should getPageById() return? Some people recommend setting it up to return an Eloquent collection. Others say just a plain array or generic object.
Ideally I feel like my scenario would best lend itself to having EloquentPageRepository grab the data from Eloquent and actually return an instance of a custom Page class that I have. For example, something along the lines of this:
<?php namespace Acme\Repositories\EloquentPageRepository;
use Acme\...\PageObject as PageObject; // Better name than PageObject?
//...
class EloquentPageRepository implements PageRepositoryInterface {
// Omitting constructor, etc.
public function getPageById($id)
{
// Grab the row via Eloquent (obviously not stored in Page:: at
// this point. I'm just using it here for clarity and time's sake.
$page = Page::find($id);
// Now we have an Eloquent collection stored in $page, but I'd
// like to return the data inside an instance of my custom class.
$pageObj = new PageObject($page->title, $page->body);
return $pageObj;
}
}
To me, this seems good because it gives a consistent delivery format from repo to repo. It also allows me to perform some constructor logic on my pageObject. Finally, it allows me to have some custom methods on the PageObject (that are repository-agnostic).
It's similar to a collection, but I don't think it's exactly that. It's basically just an instance of a class that I'm immediately populating with my database info.
My questions, listed:
Is it considered bad practice to use a repo to stuff eloquent data into a specific object and return it?
I don't want to call my class "PageObject," because that's just lame. I'd way rather call it something like "PageCollection," except for the fact that it's not actually a collection. Is there actually a name for the way that I'm using this class? It's not a collection, it's a ...? I have no idea about this, I'm just searching for any input you have.
It whole depends on what you expect from the repository pattern. Are you using the repository pattern because in the future you're going to swith of data layer and needs a new repository. If you're using Eloquent as long as your cms live then you can return an eloquent object. If you want it very flexible then make a new page object(PageComposer as mentioned in the comments). This is one of the strengts of the repository pattern so I suggest you make a PageComposer class which you instantiate and return by the repository.
Normally you can call it Page because its a page and it ships some information of a page. But that name you've already give to the Eloquent model. You can consider changing the eloquent models name and call your return object Page.
While using doctrine, I noticed that, to delete an entity, I need to retrieve that entity by given parameter(name,id etc) and then call the remove method. On the other hand, in query, I can just execute delete query.
So, seems like, using ORM style requires two operation and general sql operation require one operation. That's why, I am a little confusing, whether we should use delete(or update) operation in ORM? Isn't it worse in performance? Or Is there anything else I am missing? Can it be done in any other way in ORM style?
In Doctrine2 you can call the delete on a proxy object, which is not loaded from the database. Just create a "dummy" object, something like:
$user = $em->getPartialReference('model\User', array('id' => $id));
$em->remove($user);
It doesn't require the initial query, but I'm not quite sure if Doctrine still does it internally on flush. I don't see it in the SqlLog.
Just to add, I think this is expected behavior of any decent ORM. It deals with objects and relations. It has to know that something exists before deleting it. ORM is not just a query generator. Generally, a native query will always be faster in any ORM. Any ORM adds a layer of abstraction and it takes some time to execute it. It is a typical tradeoff, you get some fancy features and clean code, but loose some on performance.
EDIT:
I'm glad it worked out for you. Actually I stumbled on another problem, which made me realize that proxies and partial objects aren't actually the same thing. Partial objects instance the real model class, and fill it with values you want. After you initialize a partial object lazy-loading doesn't work on it anymore. So for instance, if you make a partial object with only the id, and want to delete only if another object field satisfies some condition, it will not work, because that other field will always be null.
On the other hand, proxies do work with lazy-loading, and don't share the problems that partial objects have. So I would strongly suggest not to use getPartialReference method, instead you can do something like:
$user = $em->getReference('model\User', $id);
$em->remove($user);
The getReference method returns the object if it is already loaded or a proxy if it is not. A proxy can lazy-load all the other values if/when you need them. As for your example, they will behave exactly the same, but proxies are surely a better way to go.
Done!
for me it worked like this add line 3:
$user = $em->getReference('model\User', $id);
$em->remove($user);
$em->flush();