Laravel 5.8 Likes system with AJAX Post Request Not Working - php

I am building a Like system for my recipe application in Laravel and I am not able to get my AJAX POST request to function. It just simply isn't hitting my controller, so I am not able to store any likes.
I have a relationship between three models, Like, User, Recipe.
I have an individual likes table in my DB. The code is the following:
My Models
Like
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Like extends Model
{
protected $table = 'likes';
// Get all of the recipes that are assigned this like
public function user(){
return $this->belongsTo('App\User');
}
public function recipes(){
return $this->belongsTo('App\Recipe');
}
}
User
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'username', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
public function recipes(){
return $this->hasMany('App\Recipe');
}
public function likes(){
return $this->hasMany('App\Like');
}
}
Recipe
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Recipe extends Model
{
//Table Name
protected $table = 'recipes';
// Primary Key
public $primaryKey = 'id';
// Timestamps
public $timestamps = true;
public function user(){
return $this->belongsTo('App\User');
}
public function category(){
return $this->belongsTo('App\Category');
}
public function comments(){
return $this->hasMany('App\Comment');
}
public function likes(){
return $this->hasMany('App\Like');
}
}
AJAX
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
// Likes AJAX
var recipeId = 0;
$('.like').on('click', function(event) {
event.preventDefault();
var isLike = event.target.previousElementSibling == null;
recipeId = event.target.parentNode.parentNode.dataset['recipeid'];
$.ajax({
method: 'POST',
url: urlLike,
data: {isLike: isLike, recipeId: recipeId},
success: function(data){
console.dir(data);
}
})
.done(function() {
event.target.innerText = isLike ? event.target.innerText == 'Like' ? 'You like this post' : 'Like' : event.target.innerText == 'Dislike' ? 'You don\'t like this post' : 'Dislike';
if (isLike) {
event.target.nextElementSibling.innerText = 'Dislike';
} else {
event.target.previousElementSibling.innerText = 'Like';
}
});
});
Controller Method
public function likeRecipe(Request $request){
$recipe_id = $request['recipeId'];
$is_like = $request['isLike'] === 'true';
$update = false;
$recipe = Recipe::find($recipe_id);
if (!$recipe) {
return null;
}
$user = Auth::user();
$like = $user->likes()->where('recipe_id', $recipe_id)->first();
if ($like) {
$already_like = $like->like;
$update = true;
if ($already_like == $is_like) {
$like->delete();
return null;
}
} else {
$like = new Like();
}
$like->like = $is_like;
$like->user_id = $user->id;
$like->recipe_id = $recipe->id;
if ($update) {
$like->update();
} else {
$like->save();
}
return null;
}
I'm getting various HTTP errors thrown at me as I mess with the AJAX file, but it is never working. Please help! Thank you in advance!

In this kind of situation. You need learn how to debug properly for ajax request. There are thousand reason for return 500 error.
1st step: make sure your ajax function hit your url properly. Make a simple method and dd() something.
public function likeRecipe(Request $request){
dd('Yes! it working !');
}
Go to your browser right click and Inspect then go to Network tab then you can see your request. Click on your request then look for response tab.There you can find exactly what happened.

405 means your likeRecipe function not running at all and 500 means there is any response error so try remove some of your code from likeRecipe function try again for example your first try could be:
public function likeRecipe(Request $request){
return null;
}

Related

How to obtain three level model data laravel

Updated
User model
class User extends Authenticatable
{
use HasFactory, Notifiable, HasApiTokens, HasRoles;
const MALE = 'male';
const FEMALE = 'female';
protected $guard_name = 'sanctum';
public function educationalBackgrounds()
{
return $this->hasMany("App\Models\Users\EducationalBackground", "user_id");
}
public function seminars()
{
return $this->hasMany("App\Models\Users\Seminar", "user_id");
}
}
I have child table EducationalBackground which is related to User table
class EducationalBackground extends Model
{
use HasFactory;
protected $table = 'users.educational_backgrounds';
protected $fillable = [
'user_id',
'studies_type',
'year',
'course',
];
public function user()
{
return $this->belongsTo('App\Models\User', 'user_id');
}
public function educationalAwards()
{
return $this->hasMany("App\Models\Users\EducationalAward", "educational_background_id");
}
}
And a third table that i want to access the award field
class EducationalAward extends Model
{
use HasFactory;
protected $table = 'users.educational_awards';
protected $fillable = [
'educational_background_id',
'award',
'photo',
];
public function educationalBackground()
{
return $this->belongsTo('App\Models\Users\EducationalBackground', 'educational_background_id');
}
}
I have api get route here
Route::get('/educational-background/{id}', [UserProfileController::class, 'getEducationalBackground']);
Here is my api method it works fine. But i want to go deeper and access the data of third table.
public function getEducationalBackground($id)
{
$educationalBackground = EducationalBackground::with('user')->where('user_id', $id)->get();
return response()->json($educationalBackground, 200);
}
It looks like you're not really grasping the concept of relations yet. Also, I'd advise you to look into route model binding :) What you basically want to be doing is:
public function getEducationalBackground($id)
{
$user = User::find($id);
return $user->educationalBackgrounds()->with('educationalAwards')->get();
}
Also, when you're pretty sure that whenever you want to use backgrounds, you also want to use the awards, you can add the with(...) to the model definition like so:
class EducationalBackground extends Model
{
...
protected $with = ['educationalAwards'];
}
That way, you can simplify your controller method to:
public function getEducationalBackground($id)
{
$user = User::find($id);
return $user->educationalBackgrounds;
}

Wrong values from database

So, yesterday I asked this question : How to get reports only by id of user?
So, I need to get reports from table reports with user_id which is logged.
My model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Reports extends Model
{
protected $table = 'reports';
// public $timestamps = false;
protected $fillable = [
'user_id', 'username', 'user_id_posted', 'username_posted', 'news_id','opinion_id','event_id','career_solution_id', 'subject', 'why_reporting','why_reporting_message','additional_message','private'
];
public function career_solutionReport()
{
return $this->belongsTo('App\CareerSolution','career_solution_id','id');
}
public function eventReport()
{
return $this->belongsTo('App\Event','event_id','id');
}
public function newsReport()
{
return $this->belongsTo('App\News','news_id','id');
}
public function opinionReport()
{
return $this->belongsTo('App\Opinion','opinion_id','id');
}
public function user()
{
return $this->belongsTo('App\User','user_id','id');
}
}
I'm using this line :
$reports = \App\Reports::where('user_id', Sentinel::getUser()->id)->get();
but at dd($reports);
I'm getting a few wrong values:
so, here user_id should be only 548, which is my user_id. But also I'm getting reports from user_id 542, which isn't correctly.
I guess you have your relationship setup in your Sentinel model:
public function reports()
{
return $this->hasMany(Report::class, 'user_id', 'id');
}
Then you can do this instead:
$reports = Sentinel::getUser()->reports;

Laravel not getting Accessor Trying to get property on a non-object

I'm trying to get the value of a map object to check if a user made a rating or not.
{{ $book->rated }} is returning neither false or true.
in tinker
$book->getTypeAttribute(); PHP Notice: Trying to get property of
non-object in /Applications/MAMP/htdocs/elirating/app/Book.php on line
40
This method needs to get the type of the rating, the default value is set to false
public function getTypeAttribute(){
return Rate::where('type', $this->attributes['id'])->where('user_id',auth()->user()->id) === self::RATED;
}
Book.php
use Illuminate\Database\Eloquent\Model;
use willvincent\Rateable\Rateable;
use App\User;
use App\Rate;
class Book extends Model
{
use Rateable;
const RATED = "true";
const NOT_RATED = "false";
protected $fillable = [ 'user_id', 'title', 'description'];
public function scopeGetBook($query, $book_name )
{
return $query->where('slug', $book_name );
}
public function setTitleAttribute($value)
{
$this->attributes['title'] = $value;
$this->attributes['slug'] = str_slug($value);
}
public function scopeGetBookUser($query, $user_id)
{
return $query->where('user_id', $user_id )->first();
}
public function getTypeAttribute(){
return Rate::where('type', $this->attributes['id'])->where('user_id',Auth()->user()->id) === self::RATED;
}
Rate.php
<?php
namespace App;
use App\User;
use Illuminate\Database\Eloquent\Model;
use willvincent\Rateable\Rateable;
class Rate extends Model
{
protected $fillable = [
'user_id',
'type',
'rating'
];
public $timestamps = false;
protected $table = 'ratings';
}
BookController.php (this is how the type is being set, and how im trying to retrieve the value)
public function rate(Request $request, $book_id)
{
$book = Book::find($book_id);
$rating = $book->ratings()->where('user_id', auth()->user()->id)->first();
if(is_null($rating)){
$ratings = new Rating();
$ratings->rating = $request['rating'];
$ratings->user_id = auth()->user()->id;
$ratings->type = Book::RATED;
$book->ratings()->save($ratings);
return json_encode($book);
}
else{
return response()->json(['status' => 'You already left a review']);
}
}
public function show($book_name)
{
$books = Book::with('ratings')->GetBook($book_name)->get();
$data = $books->map(function(Book $book){
$book['rated'] = $book->getTypeAttribute();
return $book;
});
return view('books.show', compact('data', $data));
}
HTML
<div id="rateYo" data-rateyo-rating="{{ $book->userSumRating or 0}}" data-rateyo-read-only="{{ $book->rated }}" > ></div>
image(the read-only attribute needs to have either true of false) it doesn't show neither.
You need to call first or get on the query in the accessor:
return Rate::where(['type' => $this->getKey(), 'user_id' => auth()->id()])->first()
Also, when using accessors, you don't call the full function, as in getTypeAttribute, you only use the attribute name, type.
// wrong
$book['rated'] = $book->getTypeAttribute();
// right
$book['rated'] = $book->type;
However, I think what you should be doing to find if a user has left a rating is to use the exists or doesntExist query functions. For example:
// this will return true/false
public function getRatedAttribute()
{
return Rate::where(['type' => $this->getKey(), 'user_id' => auth()->id()])->exists();
}
Append the attribute to the Book model:
// this will add the attribute to each book in the query result.
protected $appends = ['rated']; // or type
Then you can simply use:
$book->rated; // true if a rating exists, otherwise false.

Laravel call to a member function addEagerConstraints() on boolean

I'm getting the following error whenever i go on to a users page, its supposed to show if the authenticated user is already following the user that the profile is on.
Could this be a problem with the relationship setup, it hasMany
Stack trace
local.ERROR: Call to a member function addEagerConstraints() on
boolean {"userId":1,"email":"fakeemail#aol.com","exception":"[object]
(Symfony\Component\Debug\Exception\FatalThrowableError(code: 0):
Call to a member function addEagerConstraints() on boolean at
/Applications/MAMP/htdocs/elipost/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php:522)"}
[]
UserController.php
public function getProfile($user)
{
$users = User::with([
'posts.likes' => function($query) {
$query->whereNull('deleted_at');
$query->where('user_id', auth()->user()->id);
},
'follow',
'follow.follower'
])->with(['followers' => function($query) {
$query->with('follow.followedByMe');
$query->where('user_id', auth()->user()->id);
}])->where('name','=', $user)->get();
$user = $users->map(function(User $myuser){
return ['followedByMe' => $myuser->followers->count() == 0];
});
if (!$user) {
return redirect('404');
}
return view ('profile')->with('user', $user);
}
MyFollow(model)
<?php
class MyFollow extends Model
{
use SoftDeletes, CanFollow, CanBeFollowed;
protected $fillable = [
'user_id',
'followable_id'
];
public $timestamps = false;
protected $table = 'followables';
public function follower()
{
return $this->belongsTo('App\User', 'followable_id');
}
public function followedByMe()
{
return $this->follower->getKey() === auth()->id();
}
}
MyFollow
use Overtrue\LaravelFollow\Traits\CanFollow;
use Overtrue\LaravelFollow\Traits\CanBeFollowed;
class MyFollow extends Model
{
use SoftDeletes, CanFollow, CanBeFollowed;
protected $fillable = [
'user_id',
'followable_id'
];
public $timestamps = false;
protected $table = 'followables';
public function follower()
{
return $this->belongsTo('App\User', 'followable_id');
}
public function followedByMe()
{
return $this->follower->getKey() === auth()->id();
}
}
Post
class Post extends Authenticatable
{
protected $fillable = [
'title',
'body',
'user_id',
'created_at',
];
public function user()
{
return $this->belongsTo(User::class);
}
public function comments()
{
return $this->hasMany('App\Comment');
}
public function likes()
{
return $this->hasMany('App\Like');
}
public function likedByMe()
{
foreach($this->likes as $like) {
if ($like->user_id == auth()->id()){
return true;
}
}
return false;
}
}
Likes
<?php
namespace App;
use App\Post;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Like extends Model
{
use SoftDeletes;
protected $fillable = [
'user_id',
'post_id'
];
}
User(model)
class User extends Authenticatable
{
use Notifiable,CanFollow, CanBeFollowed;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
public function posts()
{
return $this->hasMany(Post::class);
}
public function images()
{
return $this->hasMany(GalleryImage::class, 'user_id');
}
public function likes()
{
return $this->hasMany('App\Like');
}
public function follow()
{
return $this->hasMany('App\MyFollow');
}
public function comments()
{
return $this->hasMany('App\Comment');
}
}
As Jonas Staudenmeir stated, followedByMe isn't a relationship, it's a regular function and what it does is returning a boolean. I'm confused at why you've got a follow on your user model and trying to get information from the follow's follower? Just simplify, I see too much unneeded eager loading here.
Searching by indexed elements (id) > searching by name, any day of the week
Edit:
UserController
public function getProfile(Request $request, $id)
{
//$request->user() will get you the authenticated user
$user = User::with(['posts.likes','followers','follows','followers.follows'])
->findOrFail($request->user()->id);
//This returns the authenticated user's information posts, likes, followers, follows and who follows the followers
//If you wish to get someone else's information, you just switch
//the $request->user()->id to the $id if you're working with id's, if you're
//working with names, you need to replace findOrFail($id) with ->where('name',$name')->get() and this will give you
//a collection, not a single user as the findOrFail. You will need to add a ->first() to get the first user it finds in the collection it results of
//If you're planning on getting an attribute (is_following = true) to know if
//the authenticated user is following, you can use an accessor in the User model and write this after you've fetched the instance of the User
//$user->append('is_following');
return view ('profile')->with('user', $user);
}
User Model
//Accessor
//People who this user follows
public function getIsFollowingAttribute()
{
return MyFollow::where('followable_id',$this->attributes['id'])->where('user_id',Auth()->user()->id)->count() > 0 ? true : false;
}
//Relationships
//People who this user follows
public function follow()
{
return $this->hasMany('App\MyFollow','user_id','id');
}
//People who follows this user
public function followers()
{
return $this->hasMany('App\MyFollow','followable_id','id');
}
//Posts of this user
public function posts()
{
return $this->hasMany('App\Post','user_id','id');
}
//Likes of this user, not sure about this one tho, we're not using this for now but it could come in handy for you in the future
public function likes()
{
return $this->hasManyThrough('App\Likes','App\Post','user_id','user_id','id');
}
Post Model
//Who like this post
public function likes()
{
return $this->hasMany('App\Post','user_id','id');
}
MyFollow Model
//Relationships
//People who follow this user
public function followers()
{
return $this->hasMany('App\MyFollow','followable_id','user_id');
}
//Relationships
//People who this user follows
public function follow()
{
return $this->hasMany('App\MyFollow','user_id','followable_id');
}
With the help of #abr i found a simple fix, simple solution.
MyFollow.php(model)
public function followers()
{
return $this->hasMany('App\MyFollow','followable_id','user_id');
}
//Relationships
//People who this user follows
public function follow()
{
return $this->hasMany('App\MyFollow','user_id','followable_id');
}
User.php(model)
public function getIsFollowingAttribute()
{
return MyFollow::where('followable_id',$this->attributes['id'])->where('user_id',Auth()->user()->id)->count() > 0 ? true : false;
}
public function follow()
{
return $this->hasMany('App\MyFollow');
}
UserController.php
public function getProfile($user)
{
$users = User::with(['posts.likes' => function($query) {
$query->whereNull('deleted_at');
$query->where('user_id', auth()->user()->id);
}, 'followers','follow.followers'])
->with(['followers' => function($query) {
}])->where('name','=', $user)->get();
$user = $users->map(function(User $myuser){
$myuser['followedByMe'] = $myuser->getIsFollowingAttribute();
return $myuser;
});
if(!$user){
return redirect('404');
}
return view ('profile')->with('user', $user);
}
it works now. :)

How do I call model function on create event? Laravel-5

I'm trying to create a referral url when a user is first created.
My function inside my User model looks like this:
private function make_url()
{
$url = str_random(40);
$this->referral_url->url = $url;
if ($this->save()){
return true;
}
else{
return false;
}
}
Within the model, I've tried doing this but didn't work
USER::creating(function ($this){
$this->make_url();
})
I also tried calling it in my User Controller within the create user action
public function create(UserRequest $request)
{
$data = $request->all()
$data['password']= bcrypt($request->input('password'));
if($user=User::create($data))
{
$user->make_url();
}
}
I get this error in return
Indirect modification of overloaded property App\User::$referral_url has no effect
Thanks in advance for your help guys =]
p.s: If there's a better way to go about creating referral urls please tell me.
update
My entire user model
<?php
namespace App;
use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
class User extends Model implements AuthenticatableContract, CanResetPasswordContract
{
use Authenticatable, CanResetPassword;
protected $table = 'users';
protected $fillable = [
'first_name',
'last_name',
'url',
'email',
'password',
'answer_1',
'answer_2',
'answer_3'
];
protected $hidden = ['password', 'remember_token'];
public function make_url()
{
$url = str_random(40);
$this->referral_url->url = $url;
if ($this->save()){
return true;
}
else{
return false;
}
}
public function user_info()
{
return $this->hasOne('App\UserInfo');
}
public function sec_questions()
{
return $this->hasOne('App\SecurityQuestions');
}
public function referral_url()
{
return $this->hasOne('App\ReferralUrl');
}
}
update
I modified the function in the model to look like this now.
public function make_url()
{
$url = str_random(40);
$referral_url = $this->referral_url;
$referral_url = new ReferralUrl();
$referral_url->user_id = $this->id;
$referral_url->url = $url;
if ($referral_url->save()){
return true;
}
else{
return false;
}
}
When I call
$user->make_url()
I'm able to create it and it shows up in my db, but I also get the error-
Trying to get property of non-object
Normally the creating method should be called within boot():
public static function boot() {
parent::boot();
static::creating(function ($model) {
$model->foo = 'bar';
});
}
This would then be called automatically before the model is saved for the first time.
The problem that I see with your code is that you're attempting to modify a relation which doesn't exist yet.
So to explain, the hasOne method will attempt to join the current model to the remote model (in your case a ReferralUrl model) in SQL, but it can't do that before you save your model because your model doesn't exist in the database.
With your second attempt, the ReferralUrl object is the one that is changing, so that is the one that you need to save:
public function make_url() {
$url = str_random(40);
$referral_url = $this->referral_url
$referral_url->url = $url;
if ($referral_url->save()){
return true;
} else {
return false;
}
}

Categories