Facing strange problem in Laravel Components - php

I am passing data to the view of the component by $user->profile->profile_pic when I dd in that view it shows the desired value perfectly. But when I use that in some conditions or in tags to print that value it says that Attempt to read property "profile_pic" on null. Although, It is not because I can die and dump that and that value can be seen
Usage of the component:
<x-details
:user="$user->id"
class="w-96 mt-10 py-4"
letter="{{ $user->username[0] }}"
editable="{{ Auth::user()->username == $user->username }}"
profile_pic="{{ $user->profile->profile_pic }}"
/>
The component
<?php
namespace App\View\Components;
use Illuminate\View\Component;
use App\Models\User;
use Illuminate\Support\Facades\DB;
class details extends Component
{
/**
* Create a new component instance.
*
* #return void
*/
public $user;
public function __construct($user = 1)
{
$this->user = $user;
}
/**
* Get the view / contents that represent the component.
*
* #return \Illuminate\Contracts\View\View|\Closure|string
*/
public function render()
{
$user = User::with(['profile'])->firstWhere("id", $this->user);
$pic = $user->profile->profile_pic;
return view('components.details', compact("pic"));
}
}
The view of the component
<div>
#props([
"letter" => "A",
"editable" => 0,
"profile_pic" => 0
])
{{-- #php
$src = "";
if($profile_pic) {
$src = "/uploads/$profile_pic";
} else {
$src = url("fonts/icons/avatars/$letter.svg");
}
#endphp --}}
<div>
{{-- #dd($pic) --}}
{{ $pic }}
{{-- #if(!$editable)
#else
<form id="fileUpload">
<input class="hidden" type="file" name="upload_pic" id="upload_pic">
</form>
#endif --}}
</div>
</div>

It's a common issue when you trying to dd() something in foreach, it will always dump first item only and die, so you are always confirm first item and think it work as well as expected.
In your case, there is probably some user doesn't have profile_pic or profile don't have any profile_pic related on it.
Try to use the code below to debug with it in your component.
public function render()
{
try {
$user = User::with(['profile'])->firstWhere("id", $this->user);
$pic = $user->profile->profile_pic;
return view('components.details', compact("pic"));
} catch (Exception $e) {
dd($user->profile);
}
}

Inside the component, you should use $this:
So instead of
$pic = $user->profile->profile_pic
You should do
$pic = $this->user->profile->profile_pic

Related

Run Livewire click method based on whether a checkbox is checked or not

I have a Livewire component like below
<div class="mr-3">
<input type="checkbox" name="milestoneMark" value="{{ $milestoneId }}" x-model="milestone" wire:click="mark()">
</div>
The component class have below code
class CheckMilestone extends Component
{
public $milestoneId;
public function mark()
{
$milestone = Milestone::where('id', $this->milestoneId)
->first();
$user = User::where('id', auth()->id())
->first();
$user->milestones()->attach($milestone);
}
public function unMark()
{
$milestone = Milestone::where('id', $this->milestoneId)
->first();
$user = User::where('id', auth()->id())
->first();
$user->milestones()->detach($milestone);
}
public function render()
{
return view('livewire.check-milestone');
}
}
My requirement is execute mark() method when the checkbox is unchecked and execute unMark() when the checkbox is checked
I tried below but didn't work
<div class="mr-3">
<input type="checkbox" name="milestoneMark" value="{{ $milestoneId }}" x-model="milestone" {!! 'checked' ? wire:click="mark()" : wire:click="unMark()" !!} >
</div>
I try to use as less JavaScript as possible so if you can please give me a solution without JS, but if there isn't any other way to fix this I'm okay to use JS. TIA!
You have nothing to base your checked situation from. I personally would do the following:
class CheckMilestone extends Component
{
public $milestoneId;
public bool $checked = false;
public function processMark()
{
if ($this->checked) {
$this->mark();
} else {
$this->unMark();
}
}
// Rest of your code here
}
<div class="mr-3">
<input type="checkbox" wire:model="checked" wire:change="processMark()" wire:loading.attr="disabled">
</div>
To explain; we use wire:model to link the value of the checkbox to the Livewire component. We use wire:change to detect the change event triggered when you (de)select the checkbox, and fire the processMark method. Purposely we don't change the HTML as to not cause unexpected behaviour. We use wire:loading to add the disabled attribute while the checked variable is being updated, so we can't quickly uncheck it while it's processing.

Display tree structure in laravel blade

i am trying to display tree structure in blade, and i need i little help.
So Here is my algorithm
public function fetchData($entry_id)
{
$results = TreeEntry::where('parent_entry_id', $entry_id)->get();
$treeEntryList = [];
foreach ($results as $result) {
$data = [
'id' => $result->entry_id,
'parent_entry_id' => $result->parent_entry_id,
'children' => $this->fetchData($result->entry_id)
];
$treeEntryList[] = $data;
}
And getting this kind of tree array
And trying something like this, but only first child node getting displayed, and i want to show all elements.
Any idea how to solve this? Recursion here, or maybe something else?
#extends('layouts.master')
#section('content')
{{dd($resultList)}}
<div class="container">
<br id="lang-container">
#foreach ($resultList as $result)
<div>{{$result['id']}}</div></br>
#foreach ($result['children'] as $child)
<div>{{$child['id']}}</div></br>
#endforeach
#endforeach
</div>
#endsection
#section('js')
#endsection
Idea in my mind, is to use components, but didn't really used them yet, so, not sure if this work or not. First of all, run php artisan make:component TreeEntryListing, to create the component. Then, go to the created component, and insert this piece of code (Fix it if there is any problem with it)
#foreach ($children as $child)
<div>{{$child['id']}}</div></br>
#if(isset($children['children']))
<x-tree-entry-listing children={{$children}}/> // If it had syntax error, use " for children
#endif
#endforeach
Then, go to TreeEntryListing file and add $children to its constructor:
<?php
namespace App\View\Components;
use Illuminate\View\Component;
class TreeEntryListing extends Component
{
public $children;
/**
* Create a new component instance.
*
* #param $children
*/
public function __construct($children)
{
$this->children = $children;
}
/**
* Get the view / contents that represent the component.
*
* #return \Illuminate\View\View|string
*/
public function render()
{
return view('components.tree-entry-listing');
}
}
And then, use this for the primary view:
...
#foreach ($resultList as $result)
<div>{{$result['id']}}</div></br>
<x-tree-entry-listing children={{$result['children']}} />
#endforeach
...
Hope it work!

Laravel Image Default

My user uploads a profile picture which is stored in storage/profile_picture/user1.png. I use Filesystem and Storage classes to do so.
To retrieve the image I use {!! Html::image(route('profile.thumbnail', $user->profilepic_filename), "Your Picture Here", ['class'=>'img-responsive']) !!}
In my Controller I have
public function thumbnail($filename)
{
$user = User::where('profilepicture_filename', '=', $filename)->firstOrFail();
$file = Storage::disk('local_profile')->get($user->profilepicture_filename);
//$file = URL::asset('/images/default_profilepicture.png'); //doesn't work
return (new Response($file, 200))->header('Content-Type', $mime);
}
}
I want to get a default image if the profile picture is not found or not uploaded. How can I do so?
Thanks,
K
For something like this I would just override the accessor (aka getter) on your User model.
http://laravel.com/docs/master/eloquent-mutators#accessors-and-mutators
Any database column, such as profilepicture_filename can be manipulated after it's retrieved using a get___Attribute method, where ___ is the column name in Camel Case
class User
{
/**
* #return string
*/
public function getProfilepictureFilenameAttribute()
{
if (! $this->attributes['profilepicture_filename'])) {
return '/images/default_profilepicture.png';
}
return $this->attributes['profilepicture_filename'];
}
}
Now you simply have to do
<img src="{{ asset($user->profilepicture_filename) }}">
And it will display either their picture or the default if they don't have one. You no longer need the thumbnail route.
you could just do in you view:
#if(!file_exist($file->name))
<img src="/path/to/default.png">
#else
<img src="{{$file->name}}">
#endif
or in your controller:
if(!$file)
{
$file = '.../default/blah.png';
}

Laravel -5: How do I get my form inputs to validate (among other things..)?

I have a few problems.
I can't seem to display the saved data inside my form inputs.
I can't get my urls to validate unique.
My controller code looks very redundant.
Getting the urls to validate unique takes priority though.
Also I created the table for sharing social links with a user one to many relationship. I think thats correct. If not, please correct me.
update Just a thought...I think I'm probably complicating things by only have a single 'type' column. It would be easier if I had a column for each type of link i.e facebook col, twitter col etc. I just didn't want empty columns if user didn't provide info for some services
Heres my code:
UPDATE I gave up and just added individual columns for the different types of urls and changed to a one-to-one relationship.
form
{!! Form::model($user_links,['method' => 'PATCH', 'action'=> ['UserController#update_social']]) !!}
<div class='row form-group'>
<div class='col-md-2'>
{!! Form::label('facebook', 'Facebook Username') !!}
</div>
<div class='col-md-7'>
{!! Form::text('facebook', '',['class'=>'form-control']) !!}
</div>
</div>
<div class='row form-group'>
<div class='col-md-2'>
{!! Form::label('twitter', 'Twitter Username') !!}
</div>
<div class='col-md-7'>
{!! Form::text('twitter', null,['class'=>'form-control']) !!}
</div>
</div>
<div class='row form-group'>
<div class='col-md-2'>
{!! Form::label('reddit', 'Reddit Username') !!}
</div>
<div class='col-md-7'>
{!! Form::text('reddit', null,['class'=>'form-control']) !!}
</div>
</div>
<div class='row form-group'>
<div class='col-md-3'>
{!! Form::submit('Save Changes',['class'=>'btn btn-md btn-success']) !!}
</div>
</div>
{!! Form::close() !!}
controllers
public function show_social(Request $request){
$user_links= $request->user()->links;
return view('user.edit.social_connect',compact('user_links'));
}
public function update_social(SocialRequest $request){
$facebook= Input::get('facebook');
$twitter= Input::get('twitter');
$reddit= Input::get('reddit');
$user = $request->user();
$this->validate($request,['url'=>'unique']);
if(Input::has('facebook')||Input::has('google')||Input::has('reddit')||Input::has('twitter'))
{
if($facebook != ""){
$link = new SocialLinks();
$link->type = 'facebook';
$link->url='https//www.facebook.com/'.$facebook;
$link->user_id=$user->id;
$link->save();
}
if($twitter != ""){
$link = new SocialLinks();
$link->type = 'twitter';
$link->url='https//www.twitter.com/'.$twitter;
$link->user_id=$user->id;
$link->save();
}
if($reddit != ""){
$link = new SocialLinks();
$link->type = 'reddit';
$link->url='https//www.reddit.com/user'.$reddit;
$link->user_id=$user->id;
$link->save();
}
return Redirect::back()->with('message','Your profile has been updated');
}
return Redirect::back();
}
my model file
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Eloquent;
class SocialLinks extends Eloquent{
protected $table= 'social_links';
protected $fillable=[
'type',
'url'
];
public function user()
{
return $this->belongsTo('App\User');
}
}
?>
my request
<?php
namespace App\Http\Requests;
use App\Http\Requests\Request;
class SocialRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'url'=>'unique|user_id'
];
}
}
Okay, there are a few things wrong here. Your request file is being used incorrectly. You need a model for that, unless ofcourse you're just getting "request file" and "model" mixed up. For the purpose of this comment, I'm going to use Laravel's validation method from within the controller.
Something worth noting, your "url" is not present within the form. The validation looks for a "url" parameter inside of the request, but as you do not appear to be sending that within the form, it is quite redundant. Also, when you use the "unique" validator, you need to supply a database table for it to search and check against the supplied value, in this case; url.
I've added that in, but, really it's not doing anything because the url will always be empty until you supply it in your form, so the request will always pass.
public function show_social(Request $request)
{
$user_links = $request->user()->links;
return view('user.edit.social_connect', compact('user_links'));
}
public function update_social(Request $request)
{
$facebook = $request->facebook;
$twitter = $request->twitter;
$reddit = $request->reddit;
$user = $request->user();
$this->validate($request, [
'url' => 'unique:social_links,column',
]);
if($request->has('facebook') || $request->has('google') || $request->has('reddit') || $request->has('twitter'))
{
if($facebook != ""){
$link = new SocialLinks();
$link->type = 'facebook';
$link->url = 'https//www.facebook.com/'.$facebook;
$link->user_id = $user->id;
$link->save();
}
if($twitter != ""){
$link = new SocialLinks();
$link->type = 'twitter';
$link->url = 'https//www.twitter.com/'.$twitter;
$link->user_id = $user->id;
$link->save();
}
if($reddit != ""){
$link = new SocialLinks();
$link->type = 'reddit';
$link->url = 'https//www.reddit.com/user'.$reddit;
$link->user_id = $user->id;
$link->save();
}
return Redirect::back()->with('message','Your profile has been updated');
}
return Redirect::back();
}
As you can see, I removed the type hinting for your request because what you were actually doing (from what I can tell), was type hinting the model. Your "request file" which you supplied is a model and should be in the App/ directory, and referenced using a namespace.
If you are indeed using that file as a model, then your relationship looks okay, assuming of course you've got the right foreign column setup in your database, referencing the user table.
As for your model binding not working, have you tried dumping the $user_links variable, like so: dd($user_links); - to see if it actually contains anything? As you're using a request there, I cannot tell where you're getting the information.
Hopefully this helps, if you have any more questions, feel free to ask.

Having trouble creating a Like system for comments in a Laravel Project

I'm creating a project using Laravel. Users are able to like comments. I want to display a "like" button so a user can like a comment and if the user has already liked the comment I want that button to be "unlike" so a user can unlike the liked comment
In my database I have a likes table:
| id | user_id | comment_id |
My Like Model looks like this:
class Like extends \Eloquent {
protected $fillable = ['user_id', 'comment_id'];
protected $table = 'likes';
public function owner()
{
return $this->belongsTo('Acme\Users\User', 'user_id');
}
}
Comment Model looks like this:
class Comment extends \Eloquent {
protected $fillable = ['user_id', 'post_id', 'body'];
protected $table = 'comments';
public function owner()
{
return $this->belongsTo('Acme\Users\User', 'user_id');
}
public function likes()
{
return $this->hasMany('Acme\Likes\Like');
}
}
User model:
class User extends Eloquent {
public function comments()
{
return $this->hasMany('Acme\Comments\Comment');
}
public function likes()
{
return $this->hasMany('Acme\Likes\Like');
}
}
Likes controller :
class LikesController extends \BaseController {
use CommanderTrait;
/**
* Like a comment
* #return Response
*/
public function commentLike()
{
// using a command bus. Basically making a post to the likes table assigning user_id and comment_id then redirect back
extract(Input::only('user_id', 'comment_id'));
$this->execute(new CommentLikeCommand($user_id, $comment_id));
return Redirect::back();
}
public function unlike()
{
$like = new Like;
$user = Auth::user();
$id = Input::only('comment_id');
$like->where('user_id', $user->id)->where('comment_id', $id)->first()->delete();
return Redirect::back();
}
}
In my view I'm able to get the comments via $comment, and I'm able to get likes via $comment->like have:
#foreach($post->comments as $comment)
<div class="user-comment">
<p class="comment">
{{ $comment->owner->first_name }} {{ $comment->owner->last_name }} {{ $comment->body }}
</p>
<div class="com-details">
<!-- how long ago the comment was posted -->
<div class="com-time-container">
{{ $comment->created_at->diffForHumans() }} ·
</div>
<!-- HERE IS WHERE I WANT THE LIKE AND UNLIKE BUTTONS TO DISPLAY -->
#if ($comment->likes->owner->id === $currentUser->id)
{{ Form::open(['route' => 'like']) }}
{{ Form::hidden('user_id', $currentUser->id) }}
{{ Form::hidden('comment_id', $comment->id) }}
<button type="submit" class="com-like">Like</button>
{{ Form::close() }}
#else
{{ Form::open(['route' => 'unlike']) }}
{{ Form::hidden('user_id', $currentUser->id) }}
{{ Form::hidden('comment_id', $comment->id) }}
<button type="submit" class="com-like">Unlike</button>
{{ Form::close() }}
#endif
<!-- how many users like this comment -->
<span class="likes"> · {{ $comment->likes->count() }}</span>
</div>
</div><!--user-comment end-->
#endforeach
Im trying to set up an if statement to see if the current user has liked the status but im not sure how this is done? If the user has not liked the comment yet I want the "like" button to display. If the user has liked the comment I want the "unlike" button to display. I thought I could say #if($comment->likes->owner->id === $currentUser->id) but I get Undefined property. How would I go about doing this?
$comment->likes is a Collection of Like objects. To access the owner property, you would need to iterate the collection.
However, another option is to use the available methods on the Collection to do what you need:
#if (in_array($currentUser->id, $comment->likes->lists('user_id')))
$comment->likes->lists('user_id') will return an array of all the user_id values in the Collection of Likes for the Comment. in_array() will check if the $currentUser->id is in that array.
I haven't worked much with Laravel, so my syntax and terminology might be a bit off, but it should be the right idea.
Basically, $comment->likes is a list of likes on that comment. You need to iterate through those likes and check if one of them is by the current user. If one of them is by the current user, then show the unlike button. Otherwise, show the like button.
Not sure what that'd look like in Blade, but here's some pseudo-code:
$currentUserLiked = false;
// go through all of the comment's likes
foreach ($comment->likes as $like)
{
// check if this like is by the current user
if ($like->owner->id == $currentUser->id)
{
$currentUserLiked = true;
break;
}
}
if ($currentUserLiked)
{
showUnlikeButton();
}
else
{
showLikeButton();
}

Categories