I know that one can create an object from a model that represents a particular entry in the DB by using ModelName::find($model_id). What I'm curious about is how one goes about grabbing the current ID within the model on a function - so say that I wanted to expand the User model so that it will return all of the songs uploaded by said user.
I want to just be able to call $user = User::find($user_id) to grab the relevant user, and then call the function from within the model like so: $user->list_songs();. How do I, within this function in the model (public function list_songs()), check to see what the current id is? I'm currently passing it as a variable, and this seems counter-intuitive to the idea of a model.
Within the model you can simply use $this->id so long as the model has been saved to the database.
Related
**USER MODEL**
public function post(){
return $this->hasOne('App\Post','user_id','id');
}
**WEB ROUTE**
use App\User;
Route::get('/{id}/post',function($id){
return User::find($id)->post;
});
Hi everyone, I'm fairly new to both PHP and Laravel and have been struggling a bit. I just have 2 questions for this code.
In the web routes, why doesn't post have any () beside it? It was declared a function in the user model. And.. I am unsure of how these relationships work (correct me if I am wrong) but does the code above look for a user with a specific $id and connects it with a post having a similar $user_id value?
For the first bit, it is a dynamic property, here you can find how you can actually make one yourself Laravel: create a dynamic property. They essentially work because the result is a single object search based on the id, since it doesn't have to retrieve a collection it allows itself to be accessed like an attribute of the object.
And yea pretty much on the second one. It also uses laravels models to retrieve the data from the database so that you get an object back without needing to create the repositories yourself.
There are major differences between User::find($id)->post and User::find($id)->post(). First one is returning the result of the related relations so you get the post that has user_id equal to $id.
The second one returns a query builder,so you can add more conditions. For example User::find($id)->post()->where("status", 1)->get().
Is it possible for revisionable to track changes to one-to-many relationships? For example:
Model: User.
Model: Posts. User model uses Venturecraft\Revisionable\RevisionableTrait; and have a hasMany relationship to Posts. If a post is added or updated, can this be tracked by revisionable under the User which the post belongs to?
Thanks in advance
I was able to come up with something. However it's not the most elegant solution, so it'd be great if somebody would help me to clean this up (especially that unset is bothering me).
My solution will create duplicates in the table the Model belongs to. I don't know if this is what you wanted
The first thing you need to do is to add a nullable datetime revision_at column in the appropriate table.
The trait itself is pretty simple. We make use of the models boot() method to register the models updating event. It will fire whenever a model is about to update. That's exactly what we need, since we don't want a revision the first time we are creating the model.
<?php
trait RevisionableTrait {
public static function boot()
{
parent::boot();
static::updating(function( $model ){
// Grab the original Model and unset the id
// from it, so that we don't get duplicate
// entries when we create a new model.
$attributes = $model->getOriginal();
unset( $attributes['id'] );
// Next we need to add the date the revision
// was created
$attributes['revision_at'] = new DateTime();
$revision = new static($attributes);
$revision->save();
});
}
}
The only thing we do here is to grab the original model before the new fields were assigned, unset the id to make sure we don't create a duplicate entry, set the current time for the revision_at field and save the model.
That's it basically.
can this be tracked by revisionable under the User which the post belongs to?
This is automatically done since the new revision model still belongs to the respective user,
If you want to fine-tune it you could create a dedicated table for revisions where a reference to the model is stored. However storing the properties might get a little bit harder (Maybe you could store them serialized).
Another possible improvement would be to modify the getter methods of the model in the trait. For example let all() only return models that are no revisions. Then add a withRevisions() method to grab them too. You can extract the logic from it if you take a look how Laravel handles Soft Deletes. It's exactly the same.
why not use this?
$user = User::first();
$user->posts->map(function($post) {
return $post->revisionHistory;
});
I have a question regarding how to populate objects from the Data base, it's more about the architecture than populating it self.
Let's say I have a table called receipts which has: receipt_id, user_id, issue_date, business_id, product_id abd type.
Now I want to create a Receipt class which will be populated from that table, I would do something like this:
class Receipt {
public function __construct($receipt_id = null) {
if(!is_null($receipt_id))
$this->populate($receipt_id);
}
public function populate($receipt_id){
//Get data from data base
}
public function save(){
//Saves the current receipt into the data base.
}
public static function getReceiptsFromUser($user_id){
}
}
My question is about getReceiptsFromUser, should it be static?. It makes sense for the User class to have a method called getReceipts which would return an array of receipts objects calling this static method. I think it should be static because it doesn't make any sense to create an empty receipt to generate the user's receipts. Is there any flaws in this architecture or better aproaches?
Yes, it should be static. As you already mentioned yourself, you don't need a Receipt object in order to retrieve other Receipts.
From the PHP documentation:
Declaring class properties or methods as static makes them accessible without needing an instantiation of the class.
By the way, what you're doing here is called the Active Record pattern. If you're going to use this approach, it's a good idea to create a base class (you can call it Record or something similar) that defines the shared methods (such as save, find, etc.) and let your models extend them (class Receipt extends Record, class User extends Record, etc.). This way, you don't need to repeat this code in all of your models.
A nice (PHP) example for this approach is given by the PHP ActiveRecord project. A look into their documentation should give you some ideas about how it works.
Another approach is the Data Mapper pattern. The benefit is that your models don't know anything about how they're being saved, so you have great flexibility in how you want to persist your data, instead of being tied to the limitations of ActiveRecord.
Let's say I have a Widget table and the Yii Model class that goes with it.
I want to be able to instantiate it ($tempWidget = new Widget) but somehow make sure it cannot be saved to the database. I want to use the model just for the user to test things, simulate...
Obviously, I could just avoid to call $tempWidget->save() but I'd like some kind of flag that would prevent save from saving, in case some other part of the code tries to do so.
There are a few ways to accomplish what you want. The easiest way is to modify the models beforeSave() method to prevent the model from being able to save by unsetting all the attributes using the CModel unsetAttributes method , example:
public function beforeSave(){
$this->attributes = $this->unsetAttributes();
}
This will work only if you have rules associated with this model that have required fields (at least one required field), otherwise this would create an entry in your table consisting only of the primary key (assuming PK is auto increment).
I have an index action in my users_controller that get a list of users. For each user i want to calculate the number of projects they have associated (one user => many projects). I was thinking of using a method like getProjectTotal and calling it for each user. Would I put this method in the users_controller and call it like
$this->getProjectTotal($id)
in the view?
Thanks,
Jonesy
Sure. It sounds like this is just a helper method based on the call. I do that all the time. I'll typically set the method visibility to private or at least protected to keep it from being called accidentally in a rendering scenario.
I'm still relatively new to CakePHP, but I've been using the built-in counterCache in Cake 1.2 to track the number of hasMany records for a parent Model in one of my apps. Create a field in your parent Model to store the hasMany count, and enable counterCache in the $belongsTo property for the child Model, and you're good to go. It automatically updates the counterCache count field in the parent model whenever the # of "hasMany" records increases/decreases. I like this method of tracking as it keeps the controller a little cleaner if all you need is the count without any other conditions.
Docs: http://book.cakephp.org/view/816/counterCache-Cache-your-count
Also, I'm still new to MVC, but I think if you're going to gather the count via a private/protected controller method, you'd want to call it in the controller and then send the data to the view, not perform the actual method from the view, in this scenario.
Also - yes you can make a controller method for work that isn't going to render a view - BUT - in your case you should use counterCache / a Model function since you are either fetching / counting / manipulating actual data related to the Project model and it's relationship with the User model and current logged in User specifically.
When building out my controllers I tend to stick to methods that render a view or return data for an element called from requestAction. If the method is computational or setting up variables but doesn't require a template or isn't called from an element I move it to a component / helper / model / behavior. Combined with a docblock with #requestAction in the flags for introspection and I can get a list of regular actions, and data returning actions without worrying that a controller is full of other methods.