I build a multisite-cms which is flexible in routing, for three levels deep.
So people should be able to make documents as products, pages, blogs.
There are also categories but I think for in the routing it's okay to show only the first category of the product,page,blog etc
The controller is catching the page-slug and is managing the rest
Although everything is working fine, I was wondering if there would be better options. I've seen some sollutions with storing slugs into the DB,
and catch them in routes. But I don't believe this is necessary?
// awesome
// product/awesome
// product/category/awesome
//controller site/site/page
public function page($slug1 = NULL,$slug2 = NULL,$slug3 = NULL)
{
if($slug2!=NULL&&$slug3!=NULL){
$slug = $slug3;
}else if($slug2!=NULL&&$slug3==NULL){
$slug = $slug2;
}else{
$slug = $slug1;
}
// find slug and display content
}
// routes.php
// one level
$route['(:any)'] = 'site/site/page/$1';
// two levels deep
$route['(:any)/(:any)'] = 'site/site/page/$1/$2';
// max of three levels deep
$route['(:any)/(:any)/(:any)'] = 'site/site/page/$1/$2/$3';
You can abstract the variation using different methods. Like instead of having if then else, you can have 3 different methods producing different slugs as you like.
public function LoadPage($page)
{
}
public function LoadProduct($product, $page)
{
}
public function LoadBlog($blog, $product, $page)
{
}
// Your routes could refer to individuals then
// routes.php
// one level
$route['(:any)'] = 'site/site/LoadPage/$1';
// two levels deep
$route['(:any)/(:any)'] = 'site/site/LoadProduct/$1/$2';
// max of three levels deep
$route['(:any)/(:any)/(:any)'] = 'site/site/LoadBlog/$1/$2/$3';
Related
How to explicitly say to route model binding to fetch only related categories? I have my web.php file as follows:
Route::get('/catalog/{category}', [CategoryController::class, 'index'])->name('category.index');
Route::get('/catalog/{category}/{subcategory}', [SubcategoryController::class, 'index'])->name('subcategory.index');
Route::get('/catalog/{category}/{subcategory}/{subsubcategory}', [SubsubcategoryController::class, 'index'])->name('subsubcategory.index');
Subsubcategory controller:
public function index(Category $category, Subcategory $subcategory, Subsubcategory $subsubcategory)
{
$subsubcategory->load('product')->loadCount('product');
$products = Product::where('subsubcategory_id', $subsubcategory->id)->orderByRaw('product_order = 0, product_order')->get();
return view('subsubcategory.index', compact('subsubcategory', 'products'));
}
And model in question:
public function subcategory()
{
return $this->belongsTo(Subcategory::class);
}
public function category()
{
return $this->belongsTo(Category::class);
}
public function getRouteKeyName()
{
return 'slug';
}
It works partially ok. It loads all the slugs, but the problem is, let's say I have Samsung Subsubcategory with it's parent categories like:
catalog/mobile-phones/android/samsung
Whenever I modify url from catalog/mobile-phones/android/samsung to catalog/mobile-phones/ios/samsung it works, where in fact it should not. How to handle this second scenario?
PS: it also applies if I open subcategory and change category slug. But, obviously, if upper level category does not exists, it's going to throw 404.
You may want to explore the docs a bit in regard to explicit route model binding and customizing the resolution logic to get some ideas.
https://laravel.com/docs/8.x/routing#customizing-the-resolution-logic
The following is untested and I'm making some guesses about your table structures, but I think this should give you a basic concept of how you can alter route model binding to fit your needs. The same concept could also be applied to the {subcategory} binding, but with one less relationship check.
App/Providers/RouteServiceProvider.php
public function boot()
{
// ...default code...
// add custom resolution for binding 'subsubcategory'
Route::bind('subsubcategory', function($slug, $route) {
// check to see if category exists
if ($category = Category::where('slug',$route->parameter('category'))->first()) {
// check to see if subcategory exists under category
if ($subcategory = $category->subcategories()->where('slug',$route->parameter('subcategory'))->first()) {
// check to see if subsubcategory exists under subcategory
if ($subsubcategory = $subcategory->subsubcategories()->where('slug',$slug)->first()) {
// success, proper relationship exists
return $subsubcategory;
}
}
}
// fail (404) if we get here
throw new ModelNotFoundException();
});
}
I will note, however, that this makes a number of separate database calls. There may be more efficient ways to achieve the same goal through other methods if optimization is a concern.
I am trying to get order data in order tab and profile details data in profile tab.
Is it possible to achieve ???
If Yes, then please tell me how ?
If No, then please tell me, laravel is the most advance framework of PHP, why we can't send multiple data from multiple methods in same View ?
Controller
public function GetOrders()
{
$gtord = DB::table('orders')->where('email',Session::get('email'))->get();
return view('/my-account')->with('gtord',$gtord);
}
public function ProfileEdit()
{
$data = DB::table('customers')->where('email',Session::get('email'))->first();
return view('/my-account')->with('data',$data);
}
Routes
Route::get('/my-account', 'App\Http\Controllers\CustomerController#ProfileEd');
Route::get('/my-account', 'App\Http\Controllers\CustomerController#GetOrders');
Thank you in advance
You can't have multiple routes with the same 'signature', ie method and url.
If you're just showing/hiding tabs using JS, what you can do is return the view with two variables, eg:
public function AccountView()
{
$data = DB::table('customers')->where('email',Session::get('email'))->first();
$gtord = DB::table('orders')->where('email',Session::get('email'))->get();
return view('/my-account')->with(['data' => $data, 'gtord' => $gtord]);
}
And then just use one route:
Route::get('/my-account', 'App\Http\Controllers\CustomerController#AccountView');
If the two tabs are different urls, or you're using Vue or similar you would have two distinct routes with different signatures.
First, you can't have 2 same routes with the same method. It's quite logical and necessary. Otherwise, the whole routing system would collapse.
On the other hand, you can have a function in the controller, and call the other functions to collect data.
// web.php
Route::get('/my-account', 'App\Http\Controllers\CustomerController#index');
// controller
public function index()
{
$orders = $this->getOrders();
$profile = $this->getProfiles();
return view('yourView', compact(['orders', 'profile']));
}
public function getOrders()
{
//
}
public function getProfiles()
{
//
}
BTW, it's a better practice to move custom function to models, services or traits, and keep only the functions of 7 verbs in the contoller.
I'm a bit confused about my code in Laravel. I have a member that can subscribe to one or more lessons. I have three tables: members, lessons, and lesson_member.
I created a member form, and from this form, I can register the member in the lesson (I need to get the lesson id, and I put the record in the pivot table).
Then I have the lesson form; I can create a new lesson and put inside one or more members (in this case I need the member code).
Those two functions are nearly the same, but the parameters are different. In my solution, I created two different controllers with two different functions.
LessonController
<?php
public function addMember(Request $request)
{
$lessonMember = new LessonMember();
$lessonId = $request->session()->get('lessonId', 1);
if ((!($lessonMember::where('lesson_id', '=', $lessonId)
->where('license_member_id', '=', $request->memberId)
->exists()))) {
$lessonMember->lesson_id = $lessonId;
$lessonMember->license_member_id = $request->memberId;
$lessonMember->save();
$member = LicenseMember::find($request->memberId)->member;
return response()->json(['user_saved' => $member, 'llm' => $lessonMember, 'actualMembers' => $actualMembers]);
}
}
MemberController
<?php
public function addLesson(Request $request)
{
$lessonMember = new LessonMember();
$memberId = $request->session()->get('memberId', 1);
if ((!($lessonMember::where('lesson_id', $request->lessonId)
->where('license_member_id', $memberId)
->exists()))) {
$lessonMember->lesson_id = $request->lessonId;
$lessonMember->license_member_id = $memberId;
$lessonMember->save();
$member = LicenseMember::find($memberId)->member;
return response()->json(['user_saved' => $member, 'llm' => $lessonMember]);
}
}
I have the same problem with the removeFromLesson() method and in the updateLessonMember method, but the solution should be similar. It is for sure not DRY, and I think I have to put some code in the model (or somewhere else), but I don't know how to proceed. I want to refactor to have a clean solution. I read about traits, but I don't know if it's the right way to follow.
LessonController and MemberController extends a BaseController.
Inside that base controller, create a function called add_lesson_member(you can call it whatever you want)
Then you just need to call this function using $this->add_lesson_member() inside your LessonController or MemberController.
I want to check segment of particular URL on route and based on value of segment decide it to handle to another route.Somewhat like below:
Route::get('{module}/{seg}', function(){
if (is_numeric((Request::segment(3)) {
return Route::get('{module}/{seg}',Request::segment(2) . 'Controller#index');
}else{
return Route::get('{module}/{seg}',Request::segment(2).'Controller#index' . Request::segment(3));
}
});
I don't think above code works but can anyone suggest a working code for implementing above logic in laravel?
I'd suggest adding it as an optional parameter, and handle differences in the controller. Given your code, it might look like this, for instance:
// route
Route::get('{module}/{seg}/{param?}', 'Controller#index');
// controller
public function index($module, $seg, $param = null)
{
// for dynamic index methods
if (is_numeric($param)) {
$method = 'index' . $param;
return $this->{$method}();
}
// for non-numeric third-segment params, continue here as usual
}
I would like to create a custom CMS within Codeigniter, and I need a mechanism to route general pages to a default controller - for instance:
mydomain.com/about
mydomain.com/services/maintenance
These would be routed through my pagehandler controller. The default routing behaviour in Codeigniter is of course to route to a matching controller and method, so with the above examples it would require an About controller and a Services controller. This is obviously not a practical or even sensible approach.
I've seen the following solution to place in routes.php:
$route['^(?!admin|products).*'] = "pagehandler/$0";
But this poses it's own problems I believe. For example, it simply looks for "products" in the request uri and if found routes to the Products controller - but what if we have services/products as a CMS page? Does this not then get routed to the products controller?
Is there a perfect approach to this? I don't wish to have a routing where all CMS content is prefixed with the controller name, but I also need to be able to generically override the routing for other controllers.
If you use CodeIgniter 2.0 (which has been stable enough to use for months) then you can use:
$route['404_override'] = 'pages';
This will send anything that isn't a controller, method or valid route to your pages controller. Then you can use whatever PHP you like to either show the page or show a much nicer 404 page.
Read me guide explaining how you upgrade to CodeIgniter 2.0. Also, you might be interested in using an existing CMS such as PyroCMS which is now nearing the final v1.0 and has a massive following.
You are in luck. I am developing a CMS myself and it took me ages to find a viable solution to this. Let me explain myself to make sure that we are on the same page here, but I am fairly certain that we area.
Your URLS can be formatted the following ways:
http://www.mydomain.com/about - a top level page with no category
http://www.mydomain.com/services/maintenance - a page with a parent category
http://www.mydomain.com/services/maintenace/server-maintenance - a page with a category and sub category.
In my pages controller I am using the _remap function that basically captures all requests to your controllers and lets you do what you want with them.
Here is my code, commented for your convenience:
<?php
class Pages extends Controller {
// Captures all calls to this controller
public function _remap()
{
// Get out URL segments
$segments = $this->uri->uri_string();
$segments = explode("/", $segments);
// Remove blank segments from array
foreach($segments as $key => $value) {
if($value == "" || $value == "NULL") {
unset($segments[$key]);
}
}
// Store our newly filtered array segments
$segments = array_values($segments);
// Works out what segments we have
switch (count($segments))
{
// We have a category/subcategory/page-name
case 3:
list($cat, $subcat, $page_name) = $segments;
break;
// We have a category/page-name
case 2:
list($cat, $page_name) = $segments;
$subcat = NULL;
break;
// We just have a page name, no categories. So /page-name
default:
list($page_name) = $segments;
$cat = $subcat = NULL;
break;
}
if ($cat == '' && $subcat == '') {
$page = $this->mpages->fetch_page('', '', $page_name);
} else if ($cat != '' && $subcat == '') {
$page = $this->mpages->fetch_page($cat, '', $page_name);
} else if ($category != "" && $sub_category != "") {
$page = $this->mpages->fetch_page($cat, $subcat, $page_name);
}
// $page contains your page data, do with it what you wish.
}
?>
You of course would need to modify your page fetching model function accept 3 parameters and then pass in info depending on what page type you are viewing.
In your application/config/routes.php file simply put what specific URL's you would like to route and at the very bottom put this:
/* Admin routes, login routes etc here first */
$route['(:any)'] = "pages"; // Redirect all requests except for ones defined above to the pages controller.
Let me know if you need any more clarification or downloadable example code.