I'm having an issue with soft delete. I have a feature in my app where a user can star a followed property advert. They can also unstar a property advert.
This works fine. when they unstar it, the record is soft delete. The delete_at timestamp is updated.
However, if the user tries to star it again, I get a message saying that the property has already been liked/starred. So the soft delete is being ignored? Any ideas?
StarredPropertyModel
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class StarredProperty extends Model
{
use SoftDeletes;
protected $fillable = ['property_id', 'user_id'];
public function scopeStarredProperty($query, $propertyId, $userId)
{
return $query->where('property_id', $propertyId)->where('user_id', $userId)->first();
}
}
StarredPropertyController
class StarredPropertyController extends Controller
{
public function star(Property $property, User $user, Request $request)
{
if(!$user->starredProperties()->starredProperty($property->id, $user->id))
{
return response()->json(StarredProperty::create(['property_id' => $property->id, 'user_id' => $user->id]));
}
return response()->json('You have already like this property');
}
public function unstar(Property $property, User $user, Request $request)
{
$starredProperty = $user->starredProperties()->starredProperty($property->id, $user->id);
if($starredProperty->exists())
{
$starredProperty->delete();
}
}
}
You are missing a ->get() at the end of the if that checks if starredProperty exists on the star function.
$user->starredProperties()->starredProperty($property->id, $user->id) returns a query, not a record. To get the record you still need to execute get, if there are no records then the value returned from get will be null.
Related
I have Post model and related Likes item. I am trying to return the if the related model has a row for authenticated user or not. So the tables look like:
Posts
- id
- body
- etc
Likes
- id
- user_id
- likable_type
- likeable_id
Now I am return Posts eloquent with Posts::get(), however I want to return every model with a parameter inside is_liked which shows if the authenticated user has liked that post or not.
If I use ->with('likes')->where(function($q)) {} approach, it will only return me the Post that user has liked but this is not what I want.
I want every Post object to show if the authenticated user has liked it or not. Such as: Post { id, body, is_liked }
Is there any way of achieving this beside running a for loop? What is the best way to handle such scenario?
Update
class Post extends Model {
protected $appends = ['is_liked'];
public function getIsLikedAttribute() {
return $this->has('likes.user_id', '=', Auth::id())->exists();
}
public function likes() {
return $this->morphOne('App\Like', 'likeable');
}
}
class Like extends Model {
protected $fillable = [
'user_id', 'likeable_id', 'likeable_type'
];
public function likeable() {
return $this->morphTo();
}
}
Writing through this morph works, however, $this->has('likes.user_id' part returns error
Method Illuminate\Database\Query\Builder::user_id does not exist.
You can use am accessor and add that property to the append protected variable:
protected $appends = ['is_liked'];
public function getIsLikedAttribute() {
return $this->likes()->where('user_id', '=', Auth::id())->exists();
}
Now you can also check the accessor like
$post->is_liked // bool
I created an artisan custom command and in the handle() method i need to get a few info about users.
When i run:
handle() {
$users = User::all();
foreach($users as $user) {
$this->line($user->name);
}
}
it works, but i need something like:
handle() {
$users = User::all();
foreach($users as $user) {
$this->line($user->summoner->summoner_id);
}
}
And i get Trying to get property of non-object.
If i run the same code above in a controller it works just fine.
Does anyone have an idea?
User model:
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable;
protected $fillable = [
'name', 'email', 'password',
];
protected $hidden = [
'password', 'remember_token',
];
public function summoner() {
return $this->hasOne('App\Summoner');
}
Summoner model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Summoner extends Model
{
protected $table = 'summoners';
public $timestamps = true;
public function user() {
return $this->belongsTo('App\User');
}
}
As #aynber metnioned above, if DB field user.summoner_id can be set to NULL, then there are users without related Summoner.
So you can use whereHas method of the QueryBuilder, which will check relationship summoner existence:
$users = User::whereHas('summoner')->get();
foreach($users as $user) {
$this->line($user->summoner->summoner_id);
}
Or you can check existens of the relationship summoner for every selected user, but this approach may select redundant data from DB (if you need all users with non-NULL summoner_id field):
$users = User::all();
foreach($users as $user) {
if(empty($user->summoner)){
continue;
}
$this->line($user->summoner->summoner_id);
}
You can find more information about whereHas method here:
Laravel 5.4, Querying Relationship Existence: https://laravel.com/docs/5.4/eloquent-relationships#querying-relationship-existence
The only strange thing, that as you said (if I get you right), in non-artisan "regular" controller the same code executes without errors. Possible, it's just a coincidence: may be when you've checked your code in non-CLI (command line input) controller, all users had a summoner.
I have two database tables journeys and stops that are related in a many-to-many relationship. There is also the third table journey_stop (the pivot table) for the relationship.
Models
Here is my Journey.php model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Journey extends Model
{
public function stops() {
return $this->belongsToMany('App\Stop');
}
}
and the Stop.php model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Stop extends Model
{
public function journeys(){
return $this->belongsToMany('App\Journey');
}
}
Controller
Now in my controller, I have written a method changeStop(stop, journey_id) which takes a particular journey_id and either assigns a stop to it.(that is, creates a relationship between that particular stop and the journey) or removes the stop from the journey if it already exists.
Here is the method:
public function changeStop(Request $request, $id)
{
$stop = $request->all();
$journey = Journey::find($id);
if ($journey->stops()->contains($stop->id)) {
$journey->stops()->detach($stop->id);
}else{
$journey->stops()->attach($stop->id);
}
return $journey->stops();
}
But the line with the if statement throws the error:
Trying to get property of non-object
I have also tried using DB to query the pivot table directly but it throws the same error. Here's the code with DB:
public function changeStop(Request $request, $id)
{
$stop = $request->all();
$journey = Journey::find($id);
if (
DB::table('journey_stop')->where(
'journey_id',
$id
)->where(
'stop_id',
$stop->id
)->count() > 0
) {
$journey->stops->detach($stop->id);
} else {
$journey->stops->attach($stop->id);
}
return $journey->stops();
}
Everything seems right for me. But it doesn't work. What am I doing wrong?
Thanks for your time :)
You may also use the sync method to construct many-to-many associations. The sync method accepts an array of IDs to place on the intermediate table. Any IDs that are not in the given array will be removed from the intermediate table. So, after this operation is complete, only the IDs in the given array will exist in the intermediate table
$journey->stops()->sync([$stop_id])
And to work for your above code try this:
public function changeStop(Request $request, $id)
{
$stop = $request->all(); // returns an array
$journey = Journey::find($id);
if ($journey->stops->contains('id', $stop['id'])) {
$journey->stops()->detach($stop['id']);
} else {
$journey->stops()->attach($stop['id']);
}
return $journey->stops;
}
I have a User model and a Group model, connected by a pivot table. Users can be in many groups, groups contain multiple users.
I need to retrieve all of the users that are in a given group. Here are my models and my controller code so far. I'd appreciate any help - I'm sure I'm missing something obvious. My error messages vary depending on what I try, but with the current code it's:
"BadMethodCallException in Builder.php line 1992: Call to undefined method Illuminate\Database\Query\Builder::users()"
Many thanks!
User:
class User extends Model implements AuthenticatableContract, CanResetPasswordContract
{
//BLAH BLAH
public function groups()
{
return $this->belongsToMany('App\Group');
}
}
Group:
class Group extends Model
{
protected $table = 'groups';
protected $fillable = [
'name',
'detail'
];
public function scopeName($query, $group)
{
$query->where('name', '=', $group);
}
public function users()
{
return $this->belongsToMany('App\User');
}
}
Controller Code:
public function getGroup($query)
{
// $query = "name of group" FYI
// The task of this function is to return a Datatable object containing all users in the given ($query) group
$the_group = Group::with('users')->Name($query) -> get();
dd($the_group->users);
return Datatables::of($users)->make(true);
}
class User extends Model
{
public function scopeGender($query){
return $query->where('gender', 'f');
}
}
Utilizing A Query Scope
Once the scope has been defined, you may call the scope methods when querying the model. However, you do not need to include the scope prefix when calling the method. You can even chain calls to various scopes, for example:
$users = App\User::gender()->get() or -> first(); //<-- notice that you still need to
chain get or first to fetch results.
That's one error you have in query. your query should look like this.
$the_group = Group::with('users')->Name($query) -> get();
//returns all rows. use a foreach loop.
$the_group = Group::with('users')->Name($query) -> first();
// returns first
echo $the_group -> users;
Hope this help.
I am struggling with this for a while now, but I can't figure it out how it works.
In laravel I have a few models with relationships. I wan't to have al the accounts based on the logged in user and the passed parameter for the workspace.
This is how the models looks like: (I only coppied the methods to keep it short)
The user Model:
class User extends Eloquent implements UserInterface, RemindableInterface {
public function workspaces()
{
return $this->hasMany('Workspace', 'user_id');
}
public function account()
{
return $this->hasManyThrough('account', 'Workspace', 'id', 'workspace_id');
}
}
The workspace model:
class Workspace extends Eloquent implements UserInterface, RemindableInterface {
public function account()
{
return $this->hasMany('account', 'workspace_id', 'id');
}
public function user()
{
return $this->belongsTo('User', 'user_id', 'id');
}
}
The account model
class account extends Eloquent implements UserInterface, RemindableInterface {
public function account_url()
{
return $this->hasOne('acountUrl', 'id', 'account_url_id');
}
public function workspace()
{
return $this->belongsTo('Workspace', 'workspace_id', 'id');
}
}
The account_url model
class account_url extends \Eloquent implements UserInterface, RemindableInterface {
public function account()
{
return $this->belongsToMany('account', 'id', 'account_url_id');
}
}
So I want from the logged-in user with a specific workspace all the account with the account_urls
something like this: user->workspace->account->account_url
I tried the following things but it don't work:
$account_urls = user::find( Auth::user()->id)->first()->workspaces()->where('id', '=', 1)->account()->account_url()->select('url')->get();
and:
$account_urls = account::where('workspace_id', '=', '1')->account_url()->select('url')->get();
Only when I do it like this:
$account_urls = account::find(1)->account_url()->select('url')->get();
But then I get only 1 url, but when I replase find(1) for all() I get an error?
Is there someone who can help me with this?
Tanks,
Your relations are wrong, change them to:
// User
public function account()
{
return $this->hasManyThrough('Account', 'Workspace', 'user_id', 'workspace_id');
}
// Account
// use camelCase for relations
public function accountUrl()
{
// I assume you have account_url_id on accounts table
// If it's opposite, then use hasOne
return $this->belongsTo('AcountUrl', 'account_url_id', 'id');
}
// AccountUrl (use camelCase)
public function account()
{
// if above is hasOne, then here belongsTo instead.
return $this->hasOne('account', 'account_url_id', 'id');
}
Now, fetching models:
// this part is .. amazing ;)
user::find( Auth::user()->id )->first();
// it does this:
Auth::user()->id // fetch user and get his id
user::find( .. ) // fetch user with given id, you have this user already above...
->first() // fetch first row from users table (not the one with id provided before)
so you want:
$account_urls = Auth::user()->workspaces()
->where('id', '=', 1)->first() // first fetches the result
// or simply
// ->find(1)
->accounts()->first()->accountUrl()
->pluck('url'); // this does 'SELECT url' and returns only this field instead of model
Just remember that:
$user->workspaces
$workspace->accounts
these are collections, so you can't call anything of the model on them, you need to get single model first.