How to map Eloquent with other tables? - orm

This is the first time when i use ORM, so i wondering if it is possible to map it to other tables...
For example i have currently logged user. It is connected to the POSTS table through links table. For example if i want to select posts i do sql like this:
SELECT
`posts`.`id`', `posts`.`Name`, `posts`.`Description`
FROM
`links`,
`posts`
WHERE
`links`.`user_id` = 1 AND `links`.`post_id` = `posts`.`id`
How to extend Eloquent that if i request Posts::all() it would return posts only for current user...

You can define a query scope in your POST modal
public function scopeOfUser($query,$user_id)
{
return $query->join('links', 'links.post_id', '=', 'posts.id')
->where('links.user_id', '=', $user_id)
->select('posts.id', 'posts.Name', 'posts.Description');
}
Then use it like this:
$posts = POST::OfUser(1)->get();

Your post is a bit confusing but I hope my answer addresses it as you expect.
Warning:
Please refer to Laravel's (Many to Many) Relationship documentation for details.
To make things simple and comply with the Laravel convention, I strongly suggest you to use post_userinstead of link as the pivot table (post_user table should have user_id and post_id as columns).
You don't have to define a corresponding pivot model (that's the convention).
The following models are meant to map to respectfully the tables users and posts.
User Model:
in app/model/user.php (already there, just add the relationship definition)
class User extends Eloquent {
...
public function posts()
{
return $this->belongsToMany('Post');
}
...
}
Post Model:
in app/models/post.php (to be created of course).
...
class Post extends Eloquent {
public function users()
{
return $this->belongsToMany('User');
}
}
...
Retrieval of a user's posts:
Usually, you do the following in Laravel to get the current logged user:
$user = Auth::user(); // a User model / record
Assuming $user is of type User (Model), you can retrieve all its posts using:
$user->posts(); // a Collection

Related

Laravel relationship with custom method

Table project:
project_id (int)
requestor_id (uuid)
Table requestor:
requestor_id (uuid)
Model Project:
public function requestor() {
return $this->hasOne('App\Models\Requestor', 'requestor_id', 'requestor_id');
}
Model Requestor has one method:
// this method return object users info from ldap
public function getLdapAttribute() {
$ldapWrapper = new LdapWrapper();
return $ldapWrapper->checkIfUuidExists($this->requestor_id, true);
}
Select all projects with requestor relationship:
$query = (new Project)->newQuery()->with(['requestor'])->get();
And Question is:
How can I select all projects with requestor relationship and on every requestor object call method getLdapAttribute and return all as one object?
Thank you very much :)
You can put the name of that "Ldap" attribute in the $appends array of the App\Requestor Class and Eloquent will automatically append a property named ldap with the value ,of the returned for the getLdapAttribute method.
Link to the Official Laravel Documentation for this Eloquent feature !
As SQL query of getLdapAttribute method is not specified, we can fetch projects first, and then iterate over and get that attribute.
If SQL query of getLdapAttribute is given then we can get all the data in one query (here we get attributes after the first query of fetching projects).
$projects = Project::with(['requestor'])
->get()
->each(function ($project) {
$project->requestor->getLdapAttribute();
});

Yii2 REST API relational data return

I've set up Yii2 REST API with custom actions and everything is working just fine. However, what I'm trying to do is return some data from the API which would include database relations set by foreign keys. The relations are there and they are actually working correctly. Here's an example query in one of the controllers:
$result = \app\models\Person::find()->joinWith('fKCountry', true)
->where(..some condition..)->one();
Still in the controller, I can, for example, call something like this:
$result->fKCountry->name
and it would display the appropriate name as the relation is working. So far so good, but as soon as I return the result return $result; which is received from the API clients, the fkCountry is gone and I have no way to access the name mentioned above. The only thing that remains is the value of the foreign key that points to the country table.
I can provide more code and information but I think that's enough to describe the issue. How can I encode the information from the joined data in the return so that the API clients have access to it as well?
Set it up like this
public function actionYourAction() {
return new ActiveDataProvider([
'query' => Person::find()->with('fKCountry'), // and the where() part, etc.
]);
}
Make sure that in your Person model the extraFields function includes fKCountry. If you haven't implemented the extraFields function yet, add it:
public function extraFields() {
return ['fKCountry'];
}
and then when you call the url make sure you add the expand param to tell the action you want to include the fkCountry data. So something like:
/yourcontroller/your-action?expand=fKCountry
I managed to solve the above problem.
Using ActiveDataProvider, I have 3 changes in my code to make it work.
This goes to the controller:
Model::find()
->leftJoin('table_to_join', 'table1.id = table_to_join.table1_id')
->select('table1.*, table_to_join.field_name as field_alias');
In the model, I introduced a new property with the same name as the above alias:
public $field_alias;
Still in the model class, I modified the fields() method:
public function fields()
{
$fields = array_merge(parent::fields(), ['field_alias']);
return $fields;
}
This way my API provides me the data from the joined field.
use with for Eager loading
$result = \app\models\Person::find()->with('fKCountry')
->where(..some condition..)->all();
and then add the attribute 'fkCountry' to fields array
public function fields()
{
$fields= parent::fields();
$fields[]='fkCountry';
return $fields;
}
So $result now will return a json array of person, and each person will have attribute fkCountry:{...}

Laravel 5 - creating two tables during registration

In my database design, I have two tables, People & Auth. The Auth table holds authentication information and the person_id while the People table holds all other information (name, address, etc). There is a one-to-one relationship between the tables as seen in the models below.
The reason I have separated the data into two tables is because in my
application, I will have many people who do not have authentication
capabilities (customers to the user).
App/Auth.php
class Auth extends Model implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract
{
use Authenticatable, Authorizable, CanResetPassword;
public function person() {
$this->belongsTo('Person');
}
}
App/Person.php
class Person extends Model
{
public function auth() {
$this->hasOne('Auth');
}
}
In my AuthController::create() method, I am attempting to populate both models with the user supplied information like this:
protected function create(Request $request)
{
$person = \App\Person::create($request->all());
$auth = new \App\Auth;
$auth->fill($request->all());
$auth->person_id = $person->id;
$auth->save();
return $person;
}
In my application, I would like to authorize a user and pass a $user object as the authenticated person to subsequent routes. Am I doing this correctly? Is this the best way? There's cookies and bonus points if you can also explain how to retrieve the $user object after authentication...Auth table data is not needed in the $user object.
EDIT
I have changed my config/Auth.php file to reflect the changes as noted in the answers below (thx #user3702268). However, I have now found an error with my controller. In the AuthController::create() method, I am returning my App/Person object and this throws an ErrorException seeing as how App/Person does not implement the Authorizable trait. I do not want my App/Person object to be authorizable, but it is the object that I want returned as the authenticated $user in my views. How? Shall I simply override the postRegister method or is there a more Laravel way?
EDIT 2
I'm now returning the $auth object which uses the authorizable trait. In my views/controllers I'm trying to access the Person using Auth::user()->person but getting Class 'Person' not found errors
You should replace the App\User Class in config/auth.php line 31 the class that contains the username and password:
'model' => App\User::class,
to
'model' => App\Auth::class,
Be sure to encrypt the password before saving by using the bcrypt($request->get('password')) helper or Hash::make($request->get('password')). Then you can authenticate by calling:
Auth::attempt([$request->get('username'), $request->get('password')]);
You can retrieve the authenticated user using this:
Auth::user()

Yii Many to Many Relational Query

Using a many many relational query with users having many clients and clients having many users. Trying to view a record of a particular client for a particular user. And if that client is not associated with that user, redirect to a different page.
// the relation in the client model
public function relations()
{
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
'owners'=>array(self::MANY_MANY, 'User','owner_client(owner_id, client_id)'),
);
}
//the relation in the user model
public function relations()
{
return array(
'clients'=>array(self::MANY_MANY, 'Clients','owner_client(owner_id, client_id)'),
);
}
//determine if user can view this client
//client record
$client_record = Clients::model()->findByPk($id);
//many query to find users
$users = $client_record->owners;
//if user id is not found in array, redirect
if (!in_array(Yii::app()->user->id, $users))
{
$this->redirect(array('/site/dashboard'));
}
The above code redirects, even though I know the client is related to the user logged in
When you call $users = $client_record->owners;, what you're getting back is an array of all your user models that are associated with the current client. As a result, you're comparing integers to objects, which means your in_array() condition will always fail.
What I recommend is that you build a conditional query to do your verification check. Something like this should work:
$model = Clients::model()->with(
array(
'owners'=>array(
'select'=>'owner_id',
'condition'=>'user.id = '.Yii::app()->user->id,
),
)
)->findByPk($id);
if ($model === null) {
$this->redirect(array('/site/dashboard'));
}

Laravel Relationships 2 tables

I have two tables which I want to connect,
First table = Friendship
ID
User1 = Tim
User2 = Johny
Accepted = 0/1 <- friends if accepted = 1
Second table = Rooms
ID
Owner
Room_ID
Roome_name
My goal is to get all Johny friends then check if any of them has Rooms if yes retrieve owner, room_id, room_name. I searched result in google but I could not find it. It's my first time with relationships and I don't know how to use where statments there. I would be greateful for simple and clear advice.
Here are my classes:
class Friendship extends Eloquent {
protected $table = 'friendship';
public function friendrooms()
{
return $this->hasMany('Room');
}
}
class Room extends \Eloquent {
protected $table = 'rooms';
public function roomowner()
{
return $this->belongsTo('Friendship');
}
}
Sorry for my bad english.
Assuming you have a users table, you'd want to use your friends table as a pivot table for users onto itself. It sounds quite complicated, but it ends up being pretty easy in practice...
I modified a few of your columns because there were a few things that didn't make a lot of sense. Not sure why rooms needed an id column and a room_id column. This should get you a pretty good base and it's hopefully fairly extensible for you. You'd probably want a room_user table which stores who is in what room.
Migrations
Schema::create('friends', function($table)
{
$table->increments('id');
$table->integer('user_id');
$table->integer('friend_id');
$table->boolean('accepted');
$table->boolean('deleted');
$table->timestamps();
});
Schema::create('rooms', function($table)
{
$table->increments('id');
$table->integer('user_id'); // The is room's owner.
$table->string('description');
$table->integer('room_id');
$table->string('room_name');
$table->timestamps();
});
User Model
class User extends Eloquent implements UserInterface, RemindableInterface {
public function friends()
{
return $this->belongsToMany('User', 'friends', 'user_id', 'friend_id')->wherePivot('accepted', '1');
}
public function room()
{
return $this->hasOne('Room');
}
public function hasRoom()
{
return (bool)$this->room()->count();
}
}
Use
$user = User::find(1);
foreach($user->friends as $friend) {
if($friend->hasRoom()) {
echo "<a href='javascript:location.href='ts3server://localhost/?port=9987&cid=".$friend->room->room_id."'>Join ".$friend->room->name."</a>";
}
}
If you need anymore help, ask away.
Edits:
If someone can have many rooms, simply change that relationship to a hasMany(). Then you would have to use it just a bit differently...
$user = User::find(1);
$friends = $user->friends()->paginate(15);
foreach($friends as $friend) {
if($friend->hasRoom()) {
foreach($friend->rooms as $room) {
echo "<a href='javascript:location.href='ts3server://localhost/?port=9987&cid=".$friend->room->room_id."'>Join ".$friend->room->name."</a>";
}
}
}
The logic for the 3 rooms per day doesn't really belong here. That would be more of a validation issue when allowing them to create rooms.
That's all the beauty in the Eloquent. You don't have to put a where in no place!! You just call something like
$friends = Friendship::find($friend_id);
$friends->friendrooms()->get(); //To get a list of all rooms
... yes, it's that simple!
The second you put a belongsTo or hasMany, the Eloquent already tells Laravel it should perform one kind of operation on the query: where id = 'child_id' and where = foreign_id = "id", respectively. I don't know if I have made myself clear. Any doubts, just comment! =D