Displaying json data to view as String or array - php

New to Laravel and feel that I'm missing something important here. I am uploading and saving multiple files to a folder and saving an array of the images names to the database(simple enough). Currently saving the images name in the json_encode() format so they are formatted like so["kittyTest.jpeg","kitty_2.jpeg","kitty_3.jpeg"]. So when I try and print them out to the view I get them in the json format and am trying to display them in an array formate or in some way that I can use the filename for the source image. Any help or guidance would be much appreciated.
Controller
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Form;
use DB;
class FormController extends Controller
{
public function index()
{
$images = DB::select('select * from forms');
//dd(json_decode($images[0]->filename));
return view('index', ['images'=> $images]);
}
public function create()
{
return view('create');
}
public function store(Request $request)
{
$this->validate($request, [
'filename' => 'required',
'filename.*' => 'image|mimes:jpeg,png,jpg,gif,svg|max:2048'
]);
if ($request->hasFile('filename')) {
foreach ($request->file('filename') as $image) {
$name = $image->getClientOriginalName();
$image->move(public_path().'/images/', $name);
$data[] = $name;
}
}
$form = new Form();
$form->filename = json_encode($data);
$form->save();
return back()->with('success', 'Your images have been uploaded');
}
}
View
<body>
<div class="container">
<h3 class="jumbotron">Laravel Multiple File Upload</h3>
<p>Here are the images we have in the database</p>
<ul class="list-group">
#foreach ($images as $image)
<li class="list-group-item">
{{ $image->filename }}
</li>
#endforeach
</ul>
</div>
</body>

<ul class="list-group">
#foreach ($images as $image)
#php $image_array = json_decode($image->filename,true); #endphp
#foreach ($image_array as $img)
<li class="list-group-item">
{{ $img }}
</li>
#endforeach
#endforeach
</ul>
try this and let me know

In your controller use array_map to json_decode every image filenames and then use second foreach in your view file.
Controller
public function index()
{
$images = DB::select('select * from forms');
array_map(function($a) {
$a->filename = json_decode($a->filename);
return $a;
}, $images);
return view('index', compact($images)); // you can use compact function here
}
View
<ul class="list-group">
#foreach ($images as $image)
<li class="list-group-item">
#foreach ( $image->filename as $file)
{{ $file }}
#endforeach
</li>
#endforeach
</ul>
Edit: You can also try cast to array, then you dont need to worry about mapping it in your controller file.

Related

Laravel 7 Delete array or single image from db and disk - Deletes post but not associated images from db nor disk

In Laravel 7, I am have a task management app. I can upload tasks (posts if it were a blog) and images. I have a multiple image upload working as expected. When it comes time to delete a task, the task deletes just fine but the images are left in the database and in the disk which is public into a folder called task-images. Being new to Laravel, I am struggling on how to go about this. I tried to change the settings in the filesystem.php (which I will post with the commented out code) but that didn't change the location as I had expected. In the end, I want to be able to delete the multiple images when I delete a post and also click delete on an individual image and delete that from both db and disk. I am using resource controller for all my task routes. I have no idea how to go about this and the tutorials that I have found don't really address my specific issue. Any help would be greatly appreciated. Thank you in advance.
Here is my task controller at TaskController.php
<?php
namespace App\Http\Controllers;
use App\Task;
use App\Image;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage;
class TasksController extends Controller
{
public function index()
{
$tasks = Task::orderBy('created_at', 'desc')->paginate(10);
return view('/tasks')->with('tasks', $tasks);
}
public function create()
{
return view('tasks.create');
}
public function store(Request $request)
{
$this->validate($request, [
'task_name' => 'required',
'task_description' => 'required',
]);
// Create Task
$user = Auth::user();
$task = new Task();
$data = $request->all();
$task->user_id = $user->id;
$task = $user->task()->create($data);
if ($request->hasFile('images')) {
$files = $request->file('images');
foreach ($files as $file) {
$name = time() . '-' . $file->getClientOriginalName();
$name = str_replace(' ', '-', $name);
$file->move('task-images', $name);
$task->image()->create(['name' => $name]);
$images = new Image;
$images->name = $name;
}
}
$task->task_name = $request->input('task_name');
$task->task_description = $request->input('task_description');
$task->task_priority = $request->input('task_priority');
$task->task_assigned_by = $request->input('task_assigned_by');
$task->task_assigned_to = $request->input('task_assigned_to');
$task->task_to_be_completed_date = $request->input('task_to_be_completed_date');
$task->task_notes = $request->input('task_notes');
$task->task_status = $request->task_status;
$task->save();
return redirect('/home')->with('success', 'Task Created');
}
public function edit($id)
{
$task = Task::find($id);
return view('tasks.edit', ['task' => $task]);
}
public function update(Request $request, $id)
{
$this->validate($request, [
'task_name' => 'required',
'task_description' => 'required',
]);
$task = Task::find($id);
$task->task_name = $request->input('task_name');
$task->task_description = $request->input('task_description');
$task->task_priority = $request->input('task_priority');
$task->task_assigned_by = $request->input('task_assigned_by');
$task->task_assigned_to = $request->input('task_assigned_to');
$task->task_to_be_completed_date = $request->input('task_to_be_completed_date');
$task->task_notes = $request->input('task_notes');
$task->task_status = $request->input('task_status');
if ($request->hasFile('images')) {
$files = $request->file('images');
foreach ($files as $file) {
$name = time() . '-' . $file->getClientOriginalName();
$name = str_replace(' ', '-', $name);
$file->move('task-images', $name);
$task->image()->create(['name' => $name]);
}
}
$task->update();
return redirect('/home')->with('success', 'Task Updated');
}
public function show($id)
{
$task = Task::find($id);
return view('tasks.show')->with('task', $task);
}
public function destroy($id)
{
$task = Task::findOrFail($id);
// $image = '/task-images/' . $task->image;
Storage::delete($task->image);
$task->delete();
return redirect('home')->with('success', 'Task Deleted');
}
}
filesystem.php (just the disks section)
'disks' => [
'local' => [
'driver' => 'local',
'root' => storage_path('app'),
// 'root' => public_path('task-images'),
],
...
in my individual show template, show.blade.php complete in case there is a code conflict.
#extends('layouts.master')
#section('content')
<div class="container">
Go Back
<div class="card p-3">
<div class="row">
<div class="col-md-4 col-sm-12">
<h3>Task</h3>
<p>{{ $task->task_name }}</p>
<h3>Assigned On:</h3>
<p>{{ $task->created_at->format('m/d/Y') }}</p>
<h3>Assigned To:</h3>
<p>{{ $task->task_assigned_to }}</p>
</div>
<div class="col-md-4 col-sm-12">
<h3>Task Description</h3>
<p>{{ $task->task_description }}</p>
<h3>Priority</h3>
<p>{{ $task->task_priority }}</p>
<h3>Status</h3>
<p>{{ $task->task_status }}</p>
</div>
<div class="col-md-4 col-sm-12">
<h3>Test Environment Date:</h3>
<p>{{ $task->task_to_be_completed_date }}</p>
<h3>Notes</h3>
<p>{{ $task->task_notes }}</p>
<h3>Action</h3>
<div style="display: inline;">
<a href="/tasks/{{$task->id}}/edit" class="btn btn-sm btn-primary mr-2">
<i class="fa fa-edit"></i> Edit
</a>
</div>
<form style="display: inline;" action="/tasks/{{ $task->id }}" method="POST" class="">
#csrf
#method('DELETE')
<button type="submit" class="btn btn-danger btn-sm ml-1 mr-1">
<i class="fa fa-trash"></i> Delete
</button>
</form>
</div>
<div class="col-md-12">
<h5>Images</h5>
<hr />
<div class="row">
#if($task->image->count()>0)
#for($i=0; $i < count($images = $task->image()->get()); $i++)
<div class="col-lg-4 col-md-6 col-sm-12">
<img class="w-50 mb-2" src="/task-images/{{ $images[$i]['name'] }}" alt="">
<form style="display: inline;" action="/tasks/{{ $task->name }}" method="POST" class="">
#csrf
#method('DELETE')
<button type="submit" class="btn btn-danger btn-sm ml-1 mr-1">
<i class="fa fa-trash"></i> Delete
</button>
</form>
</div>
#endfor
#else
<p>No images found</p>
#endif
</div>
<br />
</div>
</div>
</div>
</div>
<!--Modal Start-->
<div id="lightbox" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel" aria-hidden="true">
<div class="modal-dialog">
<button type="button" class="close hidden" data-dismiss="modal" aria-hidden="true">×</button>
<div class="modal-content">
<div class="modal-body">
<img class="w-100" src="" alt="" />
</div>
</div>
</div>
</div>
<!--Modal End-->
#endsection
#section('scripts')
<script>
$(document).ready(function() {
var $lightbox = $('#lightbox');
$('[data-target="#lightbox"]').on('click', function(event) {
var $img = $(this).find('img'),
src = $img.attr('src'),
alt = $img.attr('alt'),
css = {
'maxWidth': $(window).width() - 100,
'maxHeight': $(window).height() - 100
};
$lightbox.find('.close').addClass('hidden');
$lightbox.find('img').attr('src', src);
$lightbox.find('img').attr('alt', alt);
$lightbox.find('img').css(css);
});
$lightbox.on('shown.bs.modal', function (e) {
var $img = $lightbox.find('img');
$lightbox.find('.modal-dialog').css({'width': $img.width()});
$lightbox.find('.close').removeClass('hidden');
});
});
</script>
#endsection
In my Task model, Task.php, I have:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use App\Image;
class Task extends Model
{
protected $fillable = [
'task_name', 'task_priority', 'task_assigned_to', 'task_assigned_by', 'task_description', 'task_to_be_completed_date', 'task_status',
'task_notes'
];
public function user()
{
return $this->belongsTo(User::class);
}
public function image()
{
// return $this->hasMany('App\Image');
return $this->hasMany(Image::class);
}
}
and finally my Image Model Image.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use App\Task;
class Image extends Model
{
protected $fillable = [
'task_id',
'name',
];
protected $uploads = '/task-images/';
public function getFileAttribute($image)
{
return $this->uploads . $image;
}
public function task()
{
// return $this->belongsTo('App\Task', 'task_id');
return $this->belongsTo(Task::class);
}
}
If I am missing something, please let me know so I can edit my question. Again, thank you in advance for helping me with this issue. I have been scratching my head all week on this one. Cheers.
Edit
After implementing boot functions in my model as suggested below, I received an error that an invalid argument was used for foreach. I ran a dd($task); and the following image shows the result.
Final Edit
The answer below worked for my situation. I did have to edit some things to finalize the resolution:
in Task.php I changed the foreach to the following.
foreach($task->image ?: [] as $image)
I had declared image and not image in my model and that was causing a problem. Adding the ternary operator also helped the code not throw any errors.
In my TasksController.php I changed both the update and create functions with the same ternary operator as follows:
if ($request->hasFile('images')) {
$files = $request->file('images');
foreach ($files ?: [] as $file) {
$name = time() . '-' . $file->getClientOriginalName();
$name = str_replace(' ', '-', $name);
$file->move('task-images', $name);
$task->image()->create(['name' => $name]);
}
}
I hope this helps anyone else having the same issue. Thanks to #GrumpyCrouton and #lagbox for their help in resolving this as well as #user3563950
Without them, I would still by stratching my head for another couple of weeks.
on your App\Image class, implement to boot function with the following;
use Illuminate\Support\Facades\Storage;
public static function boot() {
parent::boot();
self::deleting(function($image) {
Storage::delete(Storage::path($image->name));
});
}
Also implement the boot method in App\Task class
use Illuminate\Support\Facades\Storage;
public static function boot() {
parent::boot();
self::deleting(function($task) {
foreach($task->images as $image) {
$image->delete();
}
});
}
Now on your TaskController implement the destroy method as follows;
public function destroy($id)
{
$task = Task::findOrFail($id);
$task->delete();
return redirect('home')->with('success', 'Task Deleted');
}
As a bonus, learn Laravel model binding to ease the pain of finding an instance using findOrFail()

i am try to show data from database ,everything is oky but Does not show anything

i am try to get data from database to my index page. i complete code for get data. but dose note show anything in my index page.
i import that model.
This is my controller code
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Model\admin\Product;
class UserController extends Controller
{
function userIndex()
{
$products = Product::all();
return view('user/index', compact('products'));
}
function userContact()
{
return view('user.contact');
}
}
index file code
#foreach ($products as $product)
<li>{{ $product->index }}</li>
<li><div class="intro-item">
<figure>
<img src="{{ asset('user/img/intro/1.jpg')}}" alt="#">
</figure>
<div class="product-info">
<h5>{{$product->product_name}}</h5>
<p>{{$product->product_price}}</p>
ADD TO CART
</div>
</div>
</li>
#endforeach
i do note get any error or data.

How to pass parameter to model function from view?

I have tree structure of categories. Now I have to display only that categories which is not applied in a particular business.
Code in Controller
public function edit($id)
{
try
{
$id=Crypt::decrypt($id);
$business=Business::findOrFail($id);
$business_contact_details=BusinessContactDetails::where('business_id',$id)->select('contact_no','id')->get();
$business_working_hours=BusinessWorkingHours::where('business_id',$id)->get();
$business_categories=BusinessCategories::leftJoin('categories','categories.id','=','category_id')->where('business_id',$id)->where('categories.parent_id','0')->select('categories.name as name','approved','category_id','business_categories.id as id')->get();
$categories = Categories::where('parent_id', '=', 0)->get();
return view('admin.businesses.edit',compact('business','business_contact_details','business_working_hours','categories','category_counter','business_categories'));
}
catch(DecryptException $e)
{
return view('errors.404');
}
}
Code in Model
public function subChilds(){
return $this->hasMany('App\Categories','parent_id','id')->whereNotExists(function($query){
$query->from('business_categories')->whereRaw('categories.id=business_categories.category_id')->where('business_id',2);
});
}
Code in edit View
<div class="col-md-6">
<ul id="tree1">
#foreach($categories as $category)
<li>
<input type="checkbox" value="{{$category->id}}" name="categories[]">
{{ $category->name }}
#if(count($category->subChilds($business->id)))
#include('admin.businesses.manageChildSub',['subChilds' => $category->subChilds($business->id)])
#endif
</li>
#endforeach
</ul>
</div>
Code in manageChildSub View
<ul>
#foreach($subChilds as $child)
<li>
<input type="checkbox" value="{{$child->id}}" name="categories[]">
{{ $child->name }}
#if(count($child->childs))
#include('manageChildSub',['subChilds' => $child->subChilds($business->id)])
#endif
</li>
#endforeach
</ul>
Here, You can see that I have passed business_id as 2, but I have to pass it as my current business ID. Basically I need to call the model function with ID.
After changing to this, I am not getting any subcategories.
You can try this:
public function subChilds($business_id){
return $this->hasMany('App\Categories','parent_id','id')
->whereNotExists(function($query) use ($business_id){
$query->from('business_categories')
->whereRaw('categories.id=business_categories.category_id')
->where('business_id', $business_id);
});
}

<UL> <LI> Foreach Loop Displaying all content on one <UL>

I know this is relatively simple but it's driving me absolutely insane!
a little background, my app takes an XML feed to parse, this has nested fields such as features
it's in my DB, as an obj/array, with 'feature' as the key.
now my for each loop is displaying all this data in one UL.
Code Below.
Any Help Greatly Appreciated
//model
public static function getFeatures($id){
$featuresArray = [];
$featuresquery = DB::table('properties')
->selectRaw("features")
->where('id', $id)
->get('');
$k = $featuresquery;
foreach ($k as $feat):
if (is_array($feat)) {
$featuresArray[] = (array_key_exists('features', $feat)) ? $feat['features'] : '';
}
return $feat;
endforeach;
return $featuresArray;
}
//Controller
public function details($id) {
$property = Property::findorFail($id);
$agent = Agent::where("id",$property->agent_id)
->with([
'ads'
])->first();
$template = Agent::getTemplateName($agent);
$banners = Banner::getBanners($agent->id);
$areaAndLocations = Property::getAreaAndLocations($agent->id);
$property_type = Property::getPropertyTypes($agent->id);
$query_params = Property::getQueryParams();
$features = Property::getFeatures($property->id);
return view('frontend.'.$template.'.property.details', ['template' => $template, 'banners' => $banners, 'areaAndLocations' => $areaAndLocations, 'property_type' => $property_type, 'query_params' => $query_params, 'features' => $features])
->withDetails($property)
->withAgent($agent);
}
//View
<div class="features">
#foreach($features as $feature)
<ul><li>
{!! json_encode(json_decode($feature)) !!}
</li></ul>
#endforeach
</div>
because you giving ul>li in foreach which generating ul with every iterate,
follow the below code
<div class="features">
<ul>
#foreach($features as $feature)
<li>
{!! json_encode(json_decode($feature)) !!}
</li>
#endforeach
</ul>
</div>
Move the <ul> and </ul> tags to outside the for loop. Your code is generating a new <ul>...</ul> pair for every item on the list.
Change the foreach loop like this
<div class="features">
<ul>
#foreach($features as $feature)
<li>
{!! json_encode(json_decode($feature)) !!}
</li>
#endforeach
</ul>
</div>

show article first image from database as cover image

i have the images field in Articles table and i will save all images into this field as array : images/image1.jpg,images/image2.jpg, ...
in my index page, i am getting all articles and show them in home page :
$articles = \App\Article::all();
i want to just load first image from every article as cover image and show that but the images field is string. what can i do?
#foreach($articles as $article)
<div class="article_wrapper">
<div class="article_body">
#foreach($article->images as $image)
{{ $image }}
#endforeach
</div>
</div>
#endforeach
Using a route only:
Route::any('/article/images/{id}', ['as' => 'article.image', function($id) {
$article = Article::find($id);
$images = explode(',',$article->images);
return Response::download($images[0]);
}]);
Using a controller:
namespace MyNamespace;
use Illuminate\Routing\Controller;
use App\Article;
use Response;
class ImagesController extends Controller
{
public function show($id)
{
$article = Article::find($id);
$images = explode(',',$article->images);
return Response::download($images[0]);
}
}
Your route:
Route::get('/article/images/{id}', ['as' => 'article.image', 'uses' => 'MyNamespace\ImagesController#show']);
Then in your view:
<img src="{{route('article.image', ['id' => $article->getKey()])}}" />
So that your view will look like:
#foreach($articles as $article)
<div class="article_wrapper">
<div class="article_body">
<img src="{{route('article.image', ['id' => $article->getKey()])}}" />
</div>
</div>
#endforeach
If you aren't saving the fullpath in the images field then prefix it:
return Response::download('C:\downloadpath\' . $images[0]);

Categories