Laravel Relationships 2 tables - orm

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

Related

Symfony 6 Api Platform Extension

I'm a beginner on Sf6 and i'm stuck on a problem with the doctrine extension. I try to recover some datas from an API and send them to a front-end Angular 13. My personnal project is an application for manage some garden equipments and i look for to recover datas according to the role of the user.
If the current user have ['ROLE_USER'] i want to fetch his owns datas but if the user have ['ROLE_ADMIN'] i want to fetch all the datas for this entity. I'm ever able to do it with my entity Garden but not for the equipments entity.
My relationnal logical data model:
And the code for CurrentUserExtension.php :
<?php
namespace App\Doctrine;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryItemExtensionInterface;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use App\Entity\Garden;
use App\Entity\Lawnmower;
use App\Entity\Lightning;
use App\Entity\Pool;
use App\Entity\Portal;
use App\Entity\Watering;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Security\Core\Security;
/**
* This extension makes sure normal users can only access their own Datas
*/
final class CurrentUserExtension implements QueryCollectionExtensionInterface, QueryItemExtensionInterface
{
private $security;
public function __construct(Security $security) {
$this->security = $security;
}
public function applyToCollection(QueryBuilder $queryBuilder,
QueryNameGeneratorInterface $queryNameGenerator,
string $resourceClass,
string $operationName = null): void {
$this->addWhere($queryBuilder, $resourceClass);
}
public function applyToItem(QueryBuilder $queryBuilder,
QueryNameGeneratorInterface $queryNameGenerator,
string $resourceClass,
array $identifiers,
string $operationName = null,
array $context = []): void {
$this->addWhere($queryBuilder, $resourceClass);
}
private function addWhere(QueryBuilder $queryBuilder, string $resourceClass): void {
if ($this->security->isGranted('ROLE_ADMIN')
|| null === $user = $this->security->getUser()) {
return;
}
$rootAlias = $queryBuilder->getRootAliases()[0];
switch ($resourceClass) {
case Garden::class:
$queryBuilder->andWhere(sprintf('%s.user = :current_user', $rootAlias))
->setParameter('current_user', $user);
break;
case Lawnmower::class:
case Lightning::class:
case Pool::class:
case Portal::class:
case Watering::class:
$gardenAlias = sprintf("%s_garden", $rootAlias);
$queryBuilder->innerJoin(sprintf('%s.garden', $rootAlias), $gardenAlias)
->andWhere(sprintf('%s.user = :current_user', $gardenAlias))
->setParameter('current_user', $user);
break;
}
}
}
It's my first post on Stackoverflow so feel free to say me if my post isn't formated as well. Some help will be appreciated.
Ps: As you could see in the final class CurrentUserExtension.php i'm using Api Platform.
According to the documentation of Api Platform (https://api-platform.com/docs/core/extensions/) i'm able to fetch gardens depending of the user role, the final class CurrentUserExtension work as expected. I'm looking for doing the same for the equipments entities (Watering, Lawnmower, Pool, Portal and Lightning). Notice the relation between my entities (one-to-many):
A User could have many gardens but a Garden could belong to a single User.
A Garden could have many waterings but a Watering could belong to a single Garden.
I just saw there is an error on my relationnal logical data model: the entities Lawnmower, Pool, Portal and Lightning doesn't have the property garden_user_id in their classe. But the entity Watering is ok, i have just a single foreign key garden_id.
I'm able to give you the SQL request for retrieve all the waterings for the user which have the id 2 (this request works fine):
SELECT w.id, w.garden_id, w.name, w.flow_sensor, w.pressure_sensor, w.status FROM watering AS w INNER JOIN garden AS g ON g.id = w.garden_id INNER JOIN user AS u ON u.id = g.user_id WHERE u.id = 2
I think i'm near to my goal but now i've the following error =>
"[Semantical Error] line 0, col 104 near 'o_garden INNER': Error: 'o_garden' is already defined."
Your problem can be solved with a "conception" change.
I would say I do not try to make a single api url with different behaviors based on the user.
To make this work, I advise you to do something like this :
Be careful; my answer is on Symfony 6.2, Php 8, and Api Platform 3
#[ApiResource(
operations: [
new GetCollection(),
new GetCollection(
uriTemplate: "/gardens/my-gardens"
security: "is_granted('ROLE_USER')"
controller: MyGardenController.php
)
],
security: "is_granted('ROLE_ADMIN')"
)]
class Garden {}
And inside
class MyGardenController extends AbstractController
{
public function __construct(
private Security $security,
private GardenRepository $gardenRepository,
)
{}
public function __invoke()
{
return $this->gardenRepository->findBy(['user' => $this->security->getUser());
}
}
So ! What does it do?
By default, the Garden entity is only accessible to ADMIN.
So by default, /api/gardens cant be accessed by a non admin user.
But, /api/gardens/my-gardens as a custom controller returns only a garden linked to the currently connected user.
Just call a different endpoint based on the user role on your front end.
But if you want to keep one endpoint, you could do this inside the custom controller :
public function __invoke()
{
if($this->isGranted('ROLE_ADMIN')){
return $this->gardenRepository->findAll();
}
return $this->gardenRepository->findBy(['user' => $this->security->getUser());
}

OOP design improvment in firebase and php

I have two nested documents in firebase one called adminChat and the second is TechnicalChat when I display the request for Admin he should see tech chat and vice versa for technical the question it's better to make a separate class for every one of them class AdminFeedback and class TechnicalFeedback and create get() method for every one of them or what should I do I feel that I duplicate my code twice and I want to avoid that.
public static function get($id)
{
$documents = self::$db->collection("problems/${id}/adminChat")->documents();
$messages = [];
foreach ($documents as $doc) {
if ($doc->exists()) {
array_push($messages, $doc->data());
}
}
return $messages;
}

How to make a conditional active record relationship in Yii

I have Post, Comment, and User in a Yii api. When a Post is queried, the result should be the Post data, the User that made the Post, and any Comments for that Post, and the User who made the Comment with complete User data.
The Comment table includes a created_by field which is the user_id in the User table.
To get a single Post, here is the controller:
public function actionView($id){
$post = Post::find()
->innerJoinWith('user')
->joinWith('comments')
->where(['{{post}}.id' => $id])
->asArray()
->one();
return $post;
}
This returns a single Post by id, and any Comments.
To get all posts:
public function actionIndex(){
$posts = Post::find()
->joinWith('user', $eager)
->joinWith('comments', $eager)
->orderBy('updated_at DESC')
->limit(self::MAX_ROWS)
->asArray()
->all();
return $posts;
}
In the Post model, the Comment relationship is set like this:
public function getComments()
{
return $this
->hasMany(Comment::className(), ['object_id' => 'id']);
}
So this returns Comments if thre are any, but not the complete User data for each User who commented. So I added this to getComments()
->joinWith('user u2','u2.id = comment.created_by')
Which does return the User data along with the Comment, EXCEPT.... now actionIndex() only returns Posts that have Comments.
I reviewed this SO question but didn't find a solution. How do I conditionally include the joinWith only for Posts with Comments?
I would recommend you use ->with() instead of joinWith():
public function actionIndex() {
$posts = Post::find()
->with('user')
->with('comments')
->orderBy('updated_at DESC')
->limit(self::MAX_ROWS)
->asArray()
->all();
return $posts;
}
This way, you just use the relationship you already should have declared in your Post model class. After that, also add ->with() to your comments relationship declaration in your Post model class:
public function getComments() {
return $this
->hasMany(Comment::className(), [
'object_id' => 'id',
])
->with('user');
}
This way, you should get all the posts, the user and comments with their own users.

Laravel 5 - Authenticate against two tables

I'm somewhat new to Laravel and am having issues authenticating users the way I want. I currently have a user table and a companies table in my database.
I have seen numerous examples of authenticating with multiple tables based on whether the user is an admin or not using gaurds, but I looking to authenticate all users in the following way:
User's email and password match (fields in user table)
User's status is currently active (field in user table)
User belongs to company with active status (field in companies table)
Since the time I posted this question I've been researching as much as possible and finally ended up with a satisfactory solution. I'm not sure if it is the cleanest way possible, but it works for me. If anyone else can benefit from this, here are the steps I took:
1) I added the following code to app\User.php model:
# company / user relationship
public function company() {
return $this->belongsTo('App\Company', 'comp_id');
}
# determine if company is active
public function activeCompany() {
$comp_stat = $this->company()->first(['status'])->toArray();
return ($comp_stat >= 1) ? true : false;
}
I then modified the handle method in app\Http\Middleware\Authenticate.php with the following:
public function handle($request, Closure $next, $guard = null) {
if (Auth::guard($guard)->guest()) {
if ($request->ajax() || $request->wantsJson()) {
return response('Unauthorized.', 401);
} else {
return redirect()->guest('login');
}
}
/* begin modified section */
if (Auth::check()) {
# logout if company is inactive
if (!auth()->user()->activeCompany()) {
Auth::logout();
return redirect()->guest('login')->with('comp-status-error', true);
}
# logout if user is inactive
if (auth()->user()->status != 1) {
Auth::logout();
return redirect()->guest('login')->with('user-status-error', true);
}
}
/* end modified section */
return $next($request);
}
So, technically the user already gets authenticated before the company and user status checks, which is why you have to call Auth::logout() manually. This is also the reason why it feels a little "dirty" to me, but again, I couldn't figure out any other way and so I had to do what worked! I encourage anyone to comment if they see a better way to accomplish this.

How to map Eloquent with other tables?

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