How to pass data to AppServiceProvider? - php

I'm trying to pass the $product variable in this controller to multiple view files.
function show($lang, $slug)
{
$product = Product::where('slug', $slug)->firstOrFail();
$mightAlsoLike = Product::where('slug', '!=', $slug)->MightAlsoLike()->get();
return view('shop_show')->with(['product' => $product, 'mightAlsoLike' => $mightAlsoLike]);
}
I'm using view composer in AppServiceProvider to pass the data to multiple view files but the problem is that the data in the variable needs the $slug param. how do I pass that $slug param to AppServiceProvider?
public function boot()
{
View::composer(['shop_show', 'partials/menus/right_bar'], function ($view) {
$site_slug = Route::current()->parameter('slug');
$product = Product::where('slug', $slug)->firstOrFail();
$view->with(['product' => $product]);
});
}
EDIT: the $site_slug variable returns null.
SOLVED: I defined my $slug param as {product} in my routes. changing the $site_slug variable to $site_slug = Route::current()->parameter('product'); fixed the issue.

You can use it like this
public function boot()
{
view()->share('categoryList', $categoriesList);
view()->share('packageList', $packageList);
view()->share('testList', $testList);
view()->share('testList1', $testList1);
}

I defined my $slug param as {product} in my routes. changing the $site_slug variable to $site_slug = Route::current()->parameter('product'); fixed the issue.

Related

How can I optimize my controller so it runs 1 query instead of 3

Below is the controller of an application I am making in Laravel 9. It runs the same SQL 3 times to get the id. Is there a way to optimize it, so it just runs once?
class ProductController extends Controller
{
public function index()
{
return view('products.index', [
'products' => product::paginate(6)->withQueryString()
]);
}
public function show($id, $name = null)
{
// Checks if product exists
if (!product::find($id)) {
return dd('Product not found');
}
$slug = Str::of(product::find($id)->name)->slug('-');
//Checks if product name is set
if (!$name || $name != $slug) {
return redirect()->route('products.show', [
'id' => $id,
'name' => $slug
]);
}
//if all above is coorect then return view
return view('products.show', [
'product' => product::find($id)
]);
}
}
Simply use variable $product.
class ProductController extends Controller {
public function index() {
return view('products.index', [
'products' => product::paginate(6)->withQueryString()
]);
}
public function show($id, $name = null) {
$product = product::find($id);
//Checks if product exists
if (!$product) {
return dd('Product not found');
}
$slug = Str::of($product->name)->slug('-');
//Checks if product name is set
if (!$name || $name != $slug) {
return redirect()->route('products.show', [
'id' => $id,
'name' => $slug
]);
}
//if all above is coorect then return view
return view('products.show', [
'product' => $product
]);
}
}
variable variable variable!
A variable in PHP is a name of memory location that holds data. In PHP, a variable is declared using $ sign followed by variable name.
The main way to store information in the middle of a PHP program is by
using a variable.
so a variable can stand for a value in all over of your code block!
try to define a variable for your query, then try to call it wherever your code need to use it:
$product = product::find($id);
In your public function show($id, $name = null) fetch the value of id into a variable first:
$productById = product::find($id);
Subsequently in the function use $productById everywhere instead of product::find($id).
(I just described what #lagbox commented in a more elaborated way.)

Laravel Change URL name detail

How do I make the post single URL like myweb.com/post-name instead myweb.com/post-id ? its works fine with posts id, but not with post name.
here my current route.
Route::get('/{id}', [App\http\Controllers\PostController::class, 'show']);
and here my controller.
public function show($id)
{
$post = post::find($id);
return view('detail', ['post' => $post]);
}
thank you.
That is because you are using the $id as the identifier to resolve the post object:
myweb.com/25
Then:
public function show($id) // $id = 25
{
$post = post::find($id); // find() search for the PK column, "id" by default
return view('detail', ['post' => $post]);
}
If you want to resolve the $post by a different field, do this:
public function show($name)
{
$post = post::where('name', $name)->first();
return view('detail', ['post' => $post]);
}
This should work for a route like this:
myweb.com/a-cool-post-name
As a side note, you could resolve the model automatically makin use of Route Model Binding.

Route uses Slug, but id needed for function

I'm using slugs to navigate in my site, but I need the id connected to the slug for a function.
Function:
public function categories(Request $request, $slug)
{
$categories = Category::where('slug', $slug)->get();
$announcements = Announcement::where('category_id', $request->id)->paginate(5);
$category_lists = Category::all();
return view('announcements.index', compact('announcements', 'categories', 'category_lists'));
}
This is the function where I need to get the ID. $request->id isn't working since my $request->id returns 'null'. Is there any way to get the id that's connected to the slug/DB row?
If any more information is needed please tell me.
I've tried getting it with
$announcements = Announcement::where('category_id', Category::get(id))->paginate(5);
and things alike, nothing worked.
I suppose you override the getRouteKeyName in your Category model:
public function getRouteKeyName()
{
return 'slug';
}
Then you can get the Category like this with the route model binding:
public function categories(Request $request, Category $category)
{
$announcements = Announcement::where('category_id', $category->id)->paginate(5);
$category_lists = Category::all();
return view('announcements.index', compact('announcements', 'category', 'category_lists'));
}
Change your code to
$category = Category::where('slug', $slug)->first();
$announcements = Announcement::where('category_id', $category->id)->paginate(5);
if one category has one unique slug, just use first(), instead of get() and you can get the category object and use it.

laravel getting dynamic view based on url

I have made dynamic category routes by adding custom class to my app (how i did it is here) now i need to make my blade works with this dynamic path.
Logic
based on categories deeps my url will create such as:
site.com/category/parent
site.com/category/parent/child
site.com/category/parent/child/child
etc.
so far my view is just loading for site.com/category/parent for other urls it return 404 error.
code
CategoryRouteService
class CategoryRouteService
{
private $routes = [];
public function __construct()
{
$this->determineCategoriesRoutes();
}
public function getRoute(Category $category)
{
return $this->routes[$category->id];
}
private function determineCategoriesRoutes()
{
$categories = Category::all()->keyBy('id');
foreach ($categories as $id => $category) {
$slugs = $this->determineCategorySlugs($category, $categories);
if (count($slugs) === 1) {
$this->routes[$id] = url('category/' . $slugs[0]);
}
else {
$this->routes[$id] = url('category/' . implode('/', $slugs));
}
}
}
private function determineCategorySlugs(Category $category, Collection $categories, array $slugs = [])
{
array_unshift($slugs, $category->slug);
if (!is_null($category->parent_id)) {
$slugs = $this->determineCategorySlugs($categories[$category->parent_id], $categories, $slugs);
}
return $slugs;
}
}
CategoryServiceProvider
class CategoryServiceProvider
{
public function register()
{
$this->app->singleton(CategoryRouteService::class, function ($app) {
// At this point the categories routes will be determined.
// It happens only one time even if you call the service multiple times through the container.
return new CategoryRouteService();
});
}
}
model
//get dynamic slug routes
public function getRouteAttribute()
{
$categoryRouteService = app(CategoryRouteService::class);
return $categoryRouteService->getRoute($this);
}
blade
//{{$categoryt->route}} returning routes
<a class="post-cat" href="{{$category->route}}">{{$category->title}}</a>
route
//show parent categories with posts
Route::get('/category/{slug}', 'Front\CategoryController#parent')->name('categoryparent');
controller
public function parent($slug){
$category = Category::where('slug', $slug)->with('children')->first();
$category->addView();
$posts = $category->posts()->where('publish', '=', 1)->paginate(8);
return view('front.categories.single', compact('category','posts'));
}
Note: I'm not sure about this but i think i my route is kinda static! I mean it just getting 1 slug with it while my category can goes 2, 3 or 4 slug deep and it doesn't make sense to me to make several route and keep repeating Route::get('/category/{slug}/{slug}/{slug} like that.
As I said I'm not sure about this, please share your idea and solutions if you may.
UPDATE
based on Leena Patel answer I changed my route but when I get more than 1 slug in my url it returns error:
Example
route: site.com/category/resources (works)
route: site.com/category/resources/books/ (ERROR)
route: site.com/category/resources/books/mahayana/sutra (ERROR)
error
Call to a member function addView() on null
on
$category->addView();
when I comment that it returns error for $posts part. then error for my blade where i returned category title {{$category->title}}
So basically it seem doesn't recognize this function for returning view of category routes.
here is my function
public function parent($slug){
$category = Category::where('slug', $slug)->with('children')->first();
$category->addView();
$posts = $category->posts()->where('publish', '=', 1)->paginate(8);
return view('front.categories.single', compact('category','posts'));
}
any idea?
You can try using Route Pattern like below
Route::get('/category/{slug}', 'Front\CategoryController#parent')->where('slug','.+')->name('categoryparent')
So if you have more than one slugs in your url like /category/slug1/slug2
Your addView() method will work for one record and not for Collection So add foreach loop to achieve this.
public function parent($slug){
// $slug will be `slug1/slug2`
$searchString = '/';
$posts = array();
if( strpos($slug, $searchString) !== false ) {
$slug_array = explode('/',$slug);
}
if(isset($slug_array))
{
foreach($slug_array as $slug)
{
$category = Category::where('slug', $slug)->with('children')->first();
$category->addView();
$posts_array = $category->posts()->where('publish', '=', 1)->paginate(8);
array_push($posts,$posts_array);
}
}
else
{
$category = Category::where('slug', $slug)->with('children')->first();
$category->addView();
$posts = $category->posts()->where('publish', '=', 1)->paginate(8);
}
return view('front.categories.single', compact('category','posts'));
}
Hope it helps!
Documentation : https://laravel.com/docs/4.2/routing#route-parameters
Create route
Route::get('category/{cat}', 'YourController#mymethod');
Add this to your Providers/RouteServiceProvider.php 's boot method
public function boot()
{
Route::pattern('cat', '.+'); //add this
parent::boot();
}
In your method:
public function mymethod($cat){
echo $cat; //access your route
}
You can use optional URL sections in the route and use conditionals in controllers. Try this:
In your route:
Route::get('/category/{parent?}/{child1?}/{child2?}', 'Front\CategoryController#parent')->name('categoryparent');
In your controller:
public function mymethod($category, $parent, $child1, $child2){
if(isset($child2)){
//use $category, $parent, $child1, $child2 and return view
} else if(isset($child1)){
//use $category, $parent, $child1 and return view
} else if(isset($parent)){
//use $category, $parent and return view
} else {
//return view for $category
}
}

Unable to assign variable value in model constructor

I am playing with Laravel models and I need one to return a value that is not in the db table but it comes by running a model method. This method runs a query that groups and count grouped results.
The model method works just fine but I don't seem to be able to pre-fill the $quantity variable within the constructor with something different than 0.
So this is an excerpt of the model:
public $quantity;
function __construct($attributes = array(), $exists = false) {
parent::__construct($attributes, $exists);
$this->quantity = $this->quantity();
}
public function quantity()
{
$query = DB::table('carts_shopping')
->select('cart_id', DB::raw('COUNT(*) AS quantity'))
->where('cart_id',$this->cart_id)
->groupBy('cart_id')
->first();
return ($query) ? $query->quantity : 0;
}
While this is how I am trying to retrieve the results from controller:
$cartitems = Auth::user()->cartshopping;
foreach ($cartitems as $cartitem)
{
echo $cartitem->name;
echo $cartitem->quantity;
}
As you may guess 'cartshopping' comes from the user model being related with the model excerpt I pasted.
I also noticed that quantity() method gets called and it returns 0 all the time as if $this->cart_id was empty and, changing $this-cart_id with a real value the query itself doesn't even get executed.
Thanks a lot for any suggestion you guys can share.
Have you tried accessing the properties using $this->attributes?
public $quantity;
function __construct($attributes = array(), $exists = false) {
parent::__construct($attributes, $exists);
$this->quantity = $this->quantity();
}
public function quantity() {
$query = DB::table('carts_shopping')
->select('cart_id', DB::raw('COUNT(*) AS quantity'))
->where('cart_id', $this->attributes['cart_id'])
->groupBy('cart_id')
->first();
return ($query) ? $query->quantity : 0;
}
Failing that, you could try using the Eloquent accessors, which would be the best way to do it. This would make it dynamic as well, which could be useful.
class YourModel {
// Normal model data here
public function getQuantityAttribute() {
$query = DB::table('carts_shopping')
->select('cart_id', DB::raw('COUNT(*) AS quantity'))
->where('cart_id', $this->attributes['cart_id'])
->groupBy('cart_id')
->first();
return ($query) ? $query->quantity : 0;
}
}

Categories