Laravel append attribute not working - laravel

My objective is to append average rating of each products so i can display in the front end
I have two tables one is products and another is reviews
My review model
class Review extends Model
{
protected $table = 'reviews';
public $timestamps = true;
use SoftDeletes;
protected $dates = ['deleted_at'];
protected $fillable = array('user_id', 'product_id', 'rating', 'feedback');
public function user()
{
return $this->belongsTo('App\Models\User');
}
public function product()
{
return $this->belongsTo('App\Models\Product');
}
}
My product model
protected $appends = ['average_rating','my_rating'];
// i added these accoceries inside class as per the laravel documentation
public function reviews()
{
return $this->hasMany(Review::class);
}
public function getAverageRatingAttribute(){
return round($this->reviews()->avg('rating'),1);
}
public function getMyRatingAttribute(){
//check if user loged in
if(Auth::check()){
return round($this->reviews()->where('user_id',Auth::user()->id)->avg('rating'),1);
}else{
return round($this->reviews()->where('user_id',NULL)->avg('rating'),1);
}
}
Response
[original:protected] => Array
(
[id] => 1
[user_id] => 1
[sku_code] =>
[name] => Product title
[slug] => product-title
[description] => This is loream ipsum text to check if the application is working correctly or not
[thumbnail] => images/products/thumbnail/a9fa0b28.jpg
[short_description] => This is loream ipsum to check if update working or not
[featured_image_id] =>
[category_id] => 1
[subcategory_id] =>
[price] => 259.00
[img_height] => 20
[img_width] => 10
[discount_id] =>
[featured_product] => 0
[availability] => 1
[created_at] => 2018-02-22 11:33:27
[updated_at] => 2018-02-22 13:36:21
[deleted_at] =>
[weight] => 100
[weight_unit] => kg
)
So basically this should append average rating to my product when ever i call from the controller.
But instead i m getting Only the fields available in product table. I worked with this before and worked fine back then but i do not understand why it is not working this time.
Can anyone please help me?
thank you.

I think there is a misunderstanding about how $appends works. From the official Laravel docs:
Once the attribute has been added to the appends list, it will be included in both the model's array and JSON forms.
So it will not appear in the attributes list of the model if you print the model instance itself. However, the appended attribute will appear in the results of $model->toArray() and $model->toJson(). It will also be accessible using $model->appended_attribute (or $model->average_rating in your case).

The solution to this problem is getting data like this $product->my_rating and $product->average_rating. as suggested by #tykus at laracast
https://www.laracasts.com/discuss/channels/eloquent/laravel-append-attribute-not-working

Related

Eloquent eager loading specific columns

I have two models :Product and category
which are linked by a one-to-many relationship. A category has several products. I would like to select specific columns from each model.
Here is the query I have, but I have all the columns with category_id, but I want the category name instead of id. How can I do that. Thank you in advance.
here is the method in controller
$products = Product::with('categories:id,name')->get();
if ($products) {
$response = ['api_status' => 1, 'api_message' => 'success', 'data' => $products];
return response()->json($response);
} else {
$response = ['api_status' => 0, 'api_message' => 'Error'];
return response()->json($response);
}
Here is category model
class Categorie extends Model
{
use HasFactory, SoftDeletes;
protected $fillable =['name','redirect'];
public function products()
{
return $this->hasMany(product::class);
}
}
and the product model is:
class Product extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = [
'name',
'description',
'detail', 'img',
'categorie_id', 'onSale',
'costPrice', 'inStock', 'salePrice'
];
public function categories()
{
return $this->belongsTo(Categorie::class);
}
}
here is the response:
To modify the output of your model I'd suggest using an API resource. This will give you more granular control about how a resource is returned by the API. A resource is also the best point to modify certain values.
use Illuminate\Http\Resources\Json\JsonResource;
class ProductResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'description' => $this->description,
'detail' => $this->detail,
'img' => $this->img,
'category_id' => $this->categorie->name,
'category_name' => $this->categorie->name,
'onSale' => $this->onSale,
'costPrice' => $this->costPrice,
'inStock' => $this->inStock,
'salePrice' => $this->salePrice,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
'deleted_at' => $this->deleted_at,
'categories' => $this->categories ?? null,
];
}
}
This way you can manually specify which values your response should have.
In your controller you can include the populated array in your response by manually filling the toArray method with the current request object or just by using the resolve method which basically does the previous task for you:
$response = [
'api_status' => 1,
'api_message' => 'success',
'data' => ProductResource::collection($products)->resolve()
];
You can select particular fields from the relationship but you always need to select any keys involved in the relationship:
$products = Product::with('categories:id,name')->get();
Now each Product has its 'categories' loaded and those Category models only have the id and name fields.
Importantly:
The relationship categories is named incorrectly, it should be categorie in this case as the foreign key on Product is categorie_id and it is a singular relationship, it does not return multiple results.
Product::with('categorie:id,name')->get()
If you want to keep the name categories you would have to define the foreign key used when defining the belongsTorelationship, the second argument.
If you need to transform the structure of any of this that is a different thing and you will be walking into transformers or an API Resource.
Not sure how you want your data to look but this is the structure you will have by eager loading records, so if you need a different structure then what you get you will have to show an example.

Adding a new property dynamically to model (Laravel)

I'm using Laravel 5.4 and can't seem to add a new property, here's my code including a comment section that shows the output which does not return the new attribute I added called Url.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Exercice extends Model
{
protected $table = 'exercices';
protected $connection = 'DB_V2';
protected $appends = ['Url'];
public function getUrlAttribute() {
return 'custom';
}
/*
$exercise = App\Exercice::where("idExercice", 1)->get();
dd($exercise);
#attributes: array:15 [▼
"idExercice" => 1
"image1" => "160_A.jpg"
"image2" => "a.jpg"
"thumb1" => "v.jpg"
"thumb2" => "c.jpg"
"video" => "fliqz|077fzc4f478142cea8a73e586617f8a\r\n"
"draw1" => ""
"draw2" => ""
"drawthumb1" => ""
"drawthumb2" => ""
"licno" => 1000
"idusager" => 0
"code_exercice" => "XAMP160"
"shared" => "Y"
"rank" => 99999999
]
*/
}
The value is not going to show up in a dd() of the object itself, because the attribute doesn't actually exist in the $attributes property.
If you dd() the array or json output, you should see your value:
$exercise = App\Exercice::where("idExercice", 1)->get();
dd($exercise->toArray());

Capitalized column name to retrieve data in Laravel 4

Overview:
I have this table called User
Notice that most of the column names are on StudlyCaps like every word has been capitalized.
Now, one of the problem that I've been experiencing is of course when logging in. It's mostly like Laravel doesn't like capitalized column names and such.
Here's my User Model I'll just put the relevant parts regarding on my problem.
class User extends Eloquent implements UserInterface, RemindableInterface {
protected $primaryKey = "UserID";
protected $fillable = array('Username', 'Password', 'Active');
protected $table = 'Users';
}
And here's my method where the user starts to log in.
public function postLogin() {
$validator = Validator::make(Input::all(),
array(
'username' => 'required',
'password' => 'required'
)
);
if ($validator->fails()) {
// Redirect
} else {
$auth = Auth::attempt(array(
'Username' => Input::get('username'),
'Password' => Input::get('password'),
'Active' => 1
));
if ($auth) {
return Redirect::intended('dashboard');
} else {
return Redirect::route('login')
->with('global', 'Username/Password wrong, or account not activated');
}
}
// Redirect
}
And here goes my error, it always say Username/Password wrong, or account not activated.
Any ideas on this one?
I think its best to rename all your columns 'snake_case' style. A) this will work nicely with Laravel and B) it's good practice to keep all your database table names and columns etc the same.

CakePHP Retrieving data from habtm

I haven't been able to find a solution that solves this. I am trying to query my database for a list of groups to which a user belongs. Users can be in multiple groups, and groups can have multiple users.
I have "users", "groups", and "groups_users" tables. Each has an "id" field, and the "groups_users" table has two other fields, "group_id" and "user_id".
I think all the tables are properly formed.
My User.php, and Group.php model files look like this
//app/Model/User.php
class User extends AppModel {
public $recursive = 1;
public #hasAndBelongsToMany = array(
'Group' => array(
'className' => 'Group',
'joinTable' => 'groups_users',
'foreignKey' => 'user_id',
'associationForeignKey' => 'group_id'
)
);
}
//app/Model/Group.php
class Group extends AppModel {
public $recursive = 1;
public #hasAndBelongsToMany = array(
'User' => array(
'className' => 'User',
'joinTable' => 'groups_users',
'foreignKey' => 'group_id',
'associationForeignKey' => 'user_id'
)
);
}
Then, in my Controller file I have this
//app/Controller/ProjectController.php
class ProjectController extends AppController {
public $uses = array('Groups', 'Users');
...
$this->set('groupsForUser', $this->Groups->find('all', array(
'conditions' => array('Users.UserName' => 'testuser1')
));
}
Every time I try to display the data, I get the error message
Error: ..... Unknown column 'Users.UserName' in 'where clause'
And it shows me the SQL it's trying to run:
SELECT Groups.id, ... FROM accounts.groups AS Groups WHERE Users.UserName = 'testuser1'
Obviously the association between the tables isn't happening properly and the SQL query it is sending in isn't joined properly, but I can't figure out what's going wrong here. I've tried varieties like
.....$this->Groups->Users->find(....
And stuff like that, but nothing seems to work.
Help!
I think You should use "User.UserName" instead of "Users.UserName" in your ProjectController.php because you have "User" in your associations So model name should be same.
//app/Controller/ProjectController.php
class ProjectController extends AppController {
public $uses = array('Groups', 'User');
...
$this->set('groupsForUser', $this->Groups->find('all', array(
'conditions' => array('User.UserName' => 'testuser1')
));
}
I had to take a completely different approach to the problem. It's mostly "Cake-y".
I simplified the models a bit by taking out the $recursive bit and trimmed down the HABTM part, removing the joinTable, foreignKey, and associationForeignKey bits)
The ProjectController got the code:
public $uses = array('Group', 'User');
$arrayGroupsForUser = array();
....
$results = $this->User->Find('first', array(
'conditions' => array('User.UserName' => 'testuser1')
));
foreach($results['Group'] as $result) {
array_push($arrayGroupsForUser, $result['name']);
}
$this->set('groupsForUser', $arrayGroupsForUser);
(Thank you ned stark for noting the need for a singular "User". I can't upvote your answer with my limited reputation yet.)
The "$this->set(.....);" line is to pass just the array of group names to the View to use.
This link http://rottmann.net/2012/07/cakephp-hasandbelongstomany-relationship-queries-demystified/ was a big help in helping me solve this.

How can I make the relation name independent of the related model in CI Datamapper?

When designing a relationship in Datamapper one is bound to call the relationship the same name as the related object, which is not too handy when you have something like Application_Model_User as a class name. For those of you who will rush to say that there is a configuration option with "class" key, I know. Been there tried that. It only works for getting a related object, not for updating them.
Here is a code snippet to reproduce the problem:
// User Model
class UserModel extends Datamapper
{
public $table = 'users';
public $has_many = array(
'roles' => array(
'class' => 'RoleModel',
'other_field' => 'usermodel',
'join_other_as' => 'role',
'join_self_as' => 'user',
'join_table' => 'users_roles'
),
);
}
class RoleModel extends DataMapper
{
public $table = 'roles';
public $has_many = array(
'usermodel' => array('class' => 'UserModel',
'other_field' => 'roles',
'join_other_as'=> 'user',
'join_self_as' => 'role',
'join_table' => 'users_roles' )
);
}
// controller code. Make sure you have a role with INT id = 2, and a user with INT id = 5 in your db
$user = new UserModel(2);
$role = new RoleModel(5);
$user->save($role);
This code gives an "Unable to relate usermodel with rolemodel." error, however it does work properly (meaning a new record is inserted in the join table user_roles) if the relation is renamed from "roles" to "rolemodel".
So, if there are any avid users of CI's Datamapper that could help, please let me know how to properly define relationships.
UPDATE
You can save an object as a relation using the relationship key:
$object->save( $related, $relationship_key ).
So you would need to use
$user->save($role, "roles");
See the bottom of this web page:
http://datamapper.wanwizard.eu/pages/save.html
Leaving this bit in case it helps someone else out.
It looks like you want to have a custom name on a relationship. (That's what I get after wading through all of the cynicism) -
You get to name the relationship anything that you want with the key in the relationship array. So, in the following snippet, you use book <-- this does or does not have to be the same name as the class - that's what the class key is for.
class Author extends DataMapper {
$has_many = array(
'book' => array( // YOU USE THIS KEY TO NAME THE RELATIONSHIP
'class' => 'book',
'other_field' => 'author',
'join_self_as' => 'author',
'join_other_as' => 'book',
'join_table' => 'authors_books'
)
);
}
If this is not working for you, my guess is you have something else wrong in the set up of your relationships.
http://datamapper.wanwizard.eu/pages/advancedrelations.html

Resources