Laravel 5.4 accessor with relationship returns related model - relationship

I probably missed something really obvious, but have had no luck thus far. I have two related models, Messages and Users. I'm trying to append only the 'username' of the user who created the message when retrieving all messages. I am getting a fully serialized related user model however. I understand I could $hidden in my User model, but would like to avoid that.
In my Messages model, I've created a relationship
public function createdBy()
{
return $this->belongsTo('App\User', 'created_by');
}
Then I've added
protected $appends = ['username'];
Along with a custom attribute
public function getUsernameAttribute($value)
{
$username = null;
if ($this->createdBy){
$username = $this->createdBy->username;
}
return $username;
}
When I'm getting messages through another relationship such as
return $channel->messages()->orderBy('id','desc')->get();
I end up with the following response:
[{"id":97,"to_id":"12345","message":"This is the message content","created_at":"2017-02-25 08:58:22","created_by":{"id":1,"email":"REDACTED","name":"REDACTED","username":"myusername","created_at":"2016-09-26 16:00:00","created_by":null,"updated_at":"2017-02-20 00:33:06","updated_by":null},"updated_at":"2017-02-25 08:58:22","updated_by":null,"deleted_at":null,"username":"myusername"}, ...
What am I doing wrong, or is there a better way to accomplish what I'm trying to do?

I didn't realize I was able to hide the relationship in the original model. I ended up hiding the createdBy relationship in the Messages model.
protected $hidden = [
'createdBy'
];

Related

Using Eloquent for this kind of relationship

This is what my table relationship is like:
One user may have multiple groups. One group may have multiple users.
One message maybe be only by one user and group.
I have three models in laravel.
User,Message, and Group. And my pivot table is mssg_group which stores which user sent which message to which group.
(if this design is incorrect, do tell me the better way).
How do I write relationships to access all messages before a certain time by supplying a groupID?
I think your searching for the hasManyThrough relationship.
You need a Group model with a relationship to messages. See the Laravel Docs for more info.
/**
* app/Group.php
*/
public function messages(){
return $this->hasManyThrough('App\Message','App\MessageGroup');
}
In your Message Group Model you need to create the messages relation.
/*
* app/MessageGroup.php
*/
protected $table = "mssg_grp";
public function messages(){
return $this->hasMany('App\Message','id','mssg_id');
}
In your Message model be sure to define your custom table name.
/*
* app/Message.php
*/
class Message extends Model {
protected $table = 'mssg';
}
In your controller you can eager load the date requirement.
$group_messages = Group::where('id', $group_id)->with(function($query)
{
return $query->where('created_at','>',$date);
})->get()
I haven't tested it, so this may need to be tweaked a little bit, but it should get you close to where you want to be. I haven't address retrieving users, but it should be nearly the same as getting the messages synced up. Let me know if this helps.

CakePHP Access Multiple Database Table from a Model

I am using CakePHP 2.x
There are several database tables setup :
1) Fruit
2) Vege
3) Drink
I am able to access these database tables in a CONTROLLER using this line below. With this line, I am able to access these other tables.
public $uses = array('Get', 'Fruit', 'Vege', 'Drink');
My problem is when trying to access them in a MODEL. When I try this code below, an error occurs.
App::uses('AppModel', 'Model');
class Get extends AppModel {
public function getHistory( $limit ) {
$searchLimit = $limit;
$raw = $this->Fruit->find('all')
An error occurs at the line '$this->Fruit'.
Call to a member function find() on a non-object...
Any ideas how to call multiple database tables in a single MODEL ?
As accessing all the database tables work perfectly in the CONTROLLER, I did this in the CONTROLLER instead of the MODEL. It seems much easier and straight forward to do this in CONTROLLER.
A private function is created in the CONTROLLER, and accessed by other functions using '$this->myPrivateFunctionName()'
First you should read on model associations Model associations
Second, if that doesn't fit your needs (meaning there is no clear association between models, honestly I don't see connection between Get and Fruit) you can use ClassRegistry:
$Fruit = ClassRegistry::init('Fruit');
Before you can use ClassRegistry you must add this before class definition:
App::uses('ClassRegistry', 'Utility');
Associate model by $belongsTo, $hasMany or what ever relation you want.
than access by $this->Fruit->find()

CakePHP: how do I provide hard coded data for a model instead of a table to provide it?

I presently have a model called UserLevels which defines some user levels (user, premium user, admin, etc) and some properties of them (number, description, color, etc).
I've decided I would rather hard code this, and be able to use the __() method on the names and descriptions.
How do I go about providing data to a model so that it doesn't use a database? Is there a better way to approach this?
Thank you.
Update: Full multi-field records in a model without a database
After the author made his goal more clear, this should be the solution:
How to use models without database on CakePHP and have associations?
Don't get the title wrong, read it first, the answer describes exactly what would be the solution here as well.
Solution for single field, enum like data:
If you don't use a DB table and entries are limited it is always good to use constants because you can't do a typo without causing an error somewhere and UserLevel::USER is much more clear than a random 'user' string somewhere that could mean anything.
class UserLevel extends AppModel {
public $useTable = false;
const ADMIN = 'admin';
const USER = 'user';
/* ... */
public function getUserLevels() {
return [
UserLevel::ADMIN => __('Admin'),
/* ... */
];
}
}

Calling a model method from the controller in CakePHP

Is there no way to access a Model instance as an object (as opposed to as an array) within a Model method in CakePHP? As a super-simplified example, my instinct tells me it ought to be possible to do something like this:
Bird.php
...
public function specialName()
{
$name = $this->name;
return "Oh wow! It's ".$name;
}
If I call this method from my BirdController.php like so:
public function view($id) {
if (!$id) {
throw new NotFoundException(__('Invalid bird'));
}
$this->Bird->id = $id;
$this->set('results', $this->Bird->specialName());
}
... then it displays in the view file as "Oh wow! It's Bird" rather than "Oh wow! It's Freddy" (i.e. the name of the model, not the name of the model instance).
I've tried variations on this general approach, to no avail. It seems I must access the information via an array, like so:
Bird.php
...
public function specialName($id)
{
$data = $this->findById($id);
$name = $data['Bird']['name'];
return "Oh wow! It's ".$name;
}
This seems WAY over-complicated to me. What am I missing? Ultimately I want to be able to access dependent models in my model function, e.g. get all of the associated Bird->Subspecies. It seems like this would be much easier to do working with objects.
If you would have read the documentation about models you would know that Model::$name is the name of the model. There is also Model::alias which is the name of the model when it is accessed trough associations and the association is named different than the model name.
Cakes ORM does not return results as data objects, 3.0 will do that. Most easy way to return a specifc field right now is to do this in a model:
public function specialName($id) {
$this->id = $id;
return $this->field('name');
}
Also the "Oh wow..." should go to the view where you would echo the name that you set as $result.
If you want data objects, I know there is a plugin, a behaviour that will turn the result sets into data objects. Just google it.
Ultimately I want to be able to access dependent models in my model
function, e.g. get all of the associated Bird->Subspecies.
Did you read the book? See this section. With containable you can control what exactly you want to fetch from the associated models.
I recommend you to do the blog tutorial first to get an idea of the basics of how CakePHP works.

Problem with altering model attributes in controller

Today I've got a problem when I tried using following code to alter the model attribute in the controller
function userlist($trigger = 1)
{
if($trigger == 1)
{
$this->User->useTable = 'betausers'; //'betausers' is completely the same structure as table 'users'
}
$users = $this->User->find('all');
debug($users);
}
And the model file is
class User extends AppModel
{
var $name = "User";
//var $useTable = 'betausers';
function beforeFind() //only for debug
{
debug($this->useTable);
}
}
The debug message in the model showed the userTable attribute had been changed to betausers.And It was supposed to show all records in table betausers.However,I still got the data in the users,which quite confused me.And I hope someone can show me some directions to solve this problem.
Regards
Model::useTable is only consulted during model instantiation (see the API documentation for Model::__construct). If you want to change the model's table on the fly, you should use Model::setSource:
if ( $trigger == 1 ) {
$this->User->setSource('betausers');
}
The table to use is "fixed" when the model is loaded/instantiated. At that time a DB connection object is created, the table schema is being checked and a lot of other things happen. You can change that variable later all you want, Cake is not looking at it anymore after that point.
One model should be associated with one table, and that association shouldn't change during runtime. You'll need to make another model BetaUser and dynamically change the model you're using. Or rethink your database schema, a simple flag to distinguish beta users from regular users within the users table may be better than a whole new table.

Resources