Laravel routing - shorten the urls upto only one URI segment. - laravel

It is said that shorter the URL, better the seo (atleast my client believes on it).
Now am creating website similar to watchtown.co.uk in laravel. I need to generate in such a way that the uri should not be more than one segment.
Requirement
I have following urls:
1.Need to change From:
localhost/laravelproj/public/brands/brandname/watches
to
localhost/laravelproj/public/brandname-watches.html
2.Need to change From:
localhost/laravelproj/public/brands/brandname/jewellery
to
localhost/laravelproj/public/brandname-jewellery.html
3.Need to change From:
localhost/laravelproj/public/categories/categoryname/watches
to
localhost/laravelproj/public/categoryname-watches.html
4.Need to change From:
localhost/laravelproj/public/categories/categoryname/jewellery
to
localhost/laravelproj/public/categoryname-jewellery.html
5.Need to change From:
localhost/laravelproj/public/products/productname
to
localhost/laravelproj/public/productname-watches.html
I hope you understood the pattern .
I can see watchtown.co.uk has done exactly the same (or is it any other way ?)
I created this function in controller for brands:
public function showProductListingByBrands($brandSlug) {
$brand = Brand::findBySlug($brandSlug)->first();
$products = "";
if($brand){
$products = $brand->products()->paginate(Misc::getSetting('paginate'));
}
$products = Product::findBySlug($brandSlug);
return View::make('store');
}
Now how do i manipulate it as my requirement? Im really new in laravel.
Thanks in advance.

Just to give you a brief idea.
On your route page
Route::get('/{product_name}/', array(
'as' => 'product_page',
'uses' => 'ProductPage#getProduct'
));
As you see when the user goes to the page like
Example: www.website.com/watch
it will go to the controller ProductPage with the method of getProduct, so the variable {product_name} will be passed on the controller.
Controller
public function getProduct($product_name = false) {
$product = Products::where('product_name', '=', $product_name);
// Do check product existing record
if ($product->count() == 0) {
return Redirect::route('some-page-error')
->with('failure', 'The hell are you doing?');
} else {
$product = $product->first();
return View::make('product_page')
->with('product_name', $product);
}
}
So on method getProduct, the parameter $product_name is watch
So, the method will check if the product exists or not, if not the user will be redirected to 404 page.
If not, it will be redirected to the template that you've made then pass all the product data and display it all there.
But it would be nice if you put the Route into /product/{product_name}, also it would be also good if it's product id instead of product name since product name can get redundant.
So yea.
edits
I don't know what you're trying to do and why it must be .html, but mmm.. Just wanna give you an idea. Well I don't know if my answer is a good way, someone might give better answer than me.

Related

Laravel 8 - friendly url that call multiple controllers depending on match (products, categories, pages) - How to design it?

i would like to build a route that catch clean seo friendly url and call correct controller to display page. Examples:
https://mypage.com/some-friendly-url-separated-with-dashes [PageController]
https://mypage.com/some-cool-eletronic-ipod [ProductController]
https://mypage.com/some-furniture-drawers [CategoryController]
So I have in app route:
Route::get('/{friendlyUrl}', 'RouteController#index');
Each friendly url is a unique url(string) so there is no duplicate between pages/products/categories. There is also no pattern between urls - they could be any string used in seo(only text plus dashes/ sometimes params).
Is it wise to build one db table that keeps all urls in on place with info what to call ( url | controller_name | action_name) - as an example.
Another question is - how to call different controllers depending on url used? (for above example -> RouteController catch friendly urls -finds match in db table -> then calls correct controller)
Many thanks for any help.
Have a nice day
Mark
There's two approaches you can take to this.
Proactive:
In web.php
$slugs = Product::pluck('slug');
foreach ($slugs as $slug) {
Route::get($slug, 'ProductController#index');
}
$slugs = Category::pluck('slug');
foreach ($slugs as $slug) {
Route::get($slug, 'CategoryController#index');
}
$slugs = Page::pluck('slug');
foreach ($slugs as $slug) {
Route::get($slug, 'PagesController#index');
}
Then you can determine the product in the appropriate controler via e.g.
$actualItem = Product::where('slug', request()->path())->first();
The downside to this approach is that all routes are registered on every request even if they are not used meaning you hit the database on every request to populate them. Also, routes can't be cached when using this approach.
Reactive:
In this approach you use the fallback route:
In web.php:
Route::fallback(function (Request $request) {
if (Page::where('slug', $request->path())->exists()) {
return app()->call([ PageController::class, 'index' ]);
}
if (Category::where('slug', $request->path())->exists()) {
return app()->call([ CategoryController::class, 'index' ]);
}
if (Product::where('slug', $request->path())->exists()) {
return app()->call([ ProductController::class, 'index' ]);
}
abort(404);
});
You need create a table call slugs.
Then create a unique slug (can be auto generated or specified) for each page, product, category.
slug records also have columns to get Controller and params, ex: type and id
It'd be better if you just use a prefix for each type like this:
https://mypage.com/pages/some-friendly-url-separated-with-dashes [PageController]
https://mypage.com/products/some-cool-eletronic-ipod [ProductController]
https://mypage.com/category/some-furniture-drawers [CategoryController]
Then for achieving this, create three routes like this
Route::get('pages/{friendlyUrl}', 'PageController#index');
Route::get('products/{friendlyUrl}', 'ProductController#index');
Route::get('category/{friendlyUrl}', 'CategoryController#index');
These URLs would be SEO friendly

show name instead of id in a url using laravel routing

I have defined a route in laravel 4 that looks like so :
Route::get('/books/{id}', 'HomeController#showBook');
in the url It shows /books/1 for example , now i'm asking is there a way to show the name of the book instead but to keep also the id as a parameter in the route for SEO purposes
thanks in advance
You could also do something like this:
Route::get('books/{name}', function($name){
$url = explode("-", $name);
$id = $url[0];
return "Book #$id";
});
So you can get book by id if you pass an url like: http://website.url/books/1-book-name
if your using laravel 8, this may be helpfull.
In your Controller add this
public function show(Blog $blog)
{
return view('dashboard.Blog.show',compact('blog'));
}
In your web.php add this
Route::get('blog/{blog}', [\App\Http\Controllers\BlogController::class,'show'])->name('show');
Then add this to your model (am using Blog as my Model)
public function getRouteKeyName()
{
return 'title'; // db column name you would like to appear in the url.
}
Note: Please let your column name be unique(good practice).
Result: http://127.0.0.1:8000/blog/HelloWorld .....url for a single blog
So no more http://127.0.0.1:8000/blog/1
You are welcome.
You can add as many parameters to the url as you like, like this:
Route::get('/books/{id}/{name}', 'HomeController#showBook');
Now when you want to create an url to this page you can do the following:
URL::action('HomeController#showBook', ['id' => 1, 'name' => 'My awesome book']);
Update:
If you are certain that there will never be two books with the same title, you can just use the name of the book in the url. You just need to do this:
Route::get('/books/{name}', 'HomeControllers#showBook');
In your showBook function you need to get the book from the database using the name instead of the id. I do strongly encourage to use both the id and the name though because otherwise you can get in trouble because I don't think the book name will always be unique.
You can also use model binding check more on laravel docs
For example
Route::get('book/{book:name}',[BookController::class,'getBook'])->name('book');
The name attribute in "book/{book:name}" should be unique.

Laravel Trying to get property of non-object but not sure why

I want to grab some data from a database and display on a layout page, I've basically started building a small CMS to get into Laravel and all has gone fine so far but now i'm at a wall, and can't find a solution.
I have a layout blade file like so: http://paste.laravel.com/1fB1 nothing majot but you will see i have used $page->meta_title etc in there and in my controller i have:
public function home()
{
$pages = Pages::all();
return View::make('frontend/home')->with('pages',$pages);
}
Which I have a pages model doing nothing else really like so:
class Pages extends Eloquent {
protected $table = 'pages';
}
So why is it trying to get property of non-object and I don't really want to use a foreach because this is going to be the frontend of my 'test' website so a foreach wouldn't suite.
You'll need to access these items as a multi-dimensional array if you don't want to loop through them.
$pages[0]['field_name_here']
or
$pages[1]['field_name_here']
Its a bit of a tough one to answer without knowing how you want your CMS to work.
For example, you could have a route as {pagename} in your routes.php file, then have a page controller where you would get the requested route from the variable passed in. This would then load the page you wanted using the variable
public function page( $pagename ) {
$page = Page::where('page_title', '=', $pagename)->first();
View::make('frontend/page', array( 'page' => $page ));
}
Using a route like that, and the controller, in your view you could use {{ $page->content }} to get the content of the requested page from the database and display it.
Hope this helps.
Edit: Example Route:
Route::get('{pagename}', 'PageController#page');

CodeIgniter hide post id and only slug URL

I'm working on custom blog based on CodeIgniter. Got some problems at the moment I've achieved url:
/blog/view/1/my-very-first-post
I'm not happy with that I'd like to get rid off id and "/view"
That's how my controller looks like:
function index($postId=null)
{
$this->view($postId=null);
}
function view($postId, $str_slug = '')
{
$data['title'] = ucfirst("Blog");
$data['post'] = $this->posts->get_posts($postId);
if($postId !== null)
{
$this->load->view('templates/head', $data);
$this->load->view('templates/header', $data);
$this->load->view('posts/single_view', $data);
$this->load->view('templates/footer',$data);
} else {
$this->load->view('templates/head', $data);
$this->load->view('templates/header', $data);
$this->load->view('posts/index', $data);
$this->load->view('templates/footer',$data);
}
$row = $this->db->get_where('posts', array('id' => $postId))->row();
if ($row and ! $str_slug) {
$str_slug = url_title($row->title, 'dash', TRUE);
redirect("blog/view/{$postId}/{$str_slug}");
}
}
What is the best way to achieve this?
Thanks!!
Adam
I think you're looking for the _remap() method. You can read more about it here: http://ellislab.com/codeigniter/user-guide/general/controllers.html#remapping
Your code would look something like below. You still need to implement the get_posts_by_slug() method if you still need it.
public function _remap($slug) {
$data['title'] = ucfirst("Blog");
$data['post'] = $this->posts->get_posts_by_slug($slug);
if($slug !== null)
{
$this->load->view('templates/head', $data);
$this->load->view('templates/header', $data);
$this->load->view('posts/single_view', $data);
$this->load->view('templates/footer',$data);
} else {
$this->load->view('templates/head', $data);
$this->load->view('templates/header', $data);
$this->load->view('posts/index', $data);
$this->load->view('templates/footer',$data);
}
$row = $this->db->get_where('posts', array('slug' => $slug))->row();
}
Update:
Hey #AdamLesniak - it's generally good design to store a permalink to a blog post or a news article, so that it's not dependent on the title or any other volatile data structure in order to still be accessible.
But for a different approach, personally, I really think this is a nice system:
http://localhost/blog/my-news-article-title-permalink/3
The main issue is that if you don't store the slug/permalink somewhere, then you're going to feel sad when somebody changes the title of an article. Example, my article called "Hello World First Blog Post", which is accessible at:
http://localhost/blog/hello-world-first-blog-post
Changes into "First post I ever made":
http://localhost/blog/first-post-i-ever-made
So what happens to the initial URL? It's no longer accessible - any user that comes on the website via a search engine, or through somebody's link will now no longer see your comment and will instead get a 404 page, which you want to avoid.
A problem with using permalinks on their own is that you need to make sure they're unique, and extra constraints need to be set in place for that.
There are a few tricks that you can do, but they all have their pitfalls. You can for instance use the system I've mentioned above, where you stick the unique identifier at the end of the URL:
http://localhost/blog/hello-world/3
And if the title changes, you don't really care, because you're not using the slug to make your searches, but instead, you're relying on the unique identifier.
http://localhost/blog/first-post-i-ever-made/3
However I've seen opinions that this sort of system is against the idea of an URL (Uniform Resource Locator). I've used it in back when I was starting out, and it proved to be a flexible system; it's definitely nice to experiment with at least.
BBC do a variant of the above, by keeping the category that an article belongs to and the unique identifier for the entry:
http://www.bbc.co.uk/news/business-24511283
Basically, they know that an article will never change its category, although it may change its title, so they just keep the general topic, which is business and the unique identifier 24511283.
In the end, what I suggest you do, as it's by far the most scalable solution is to just generate the following format:
http://localhost/blog/permalink/unique-id
The format above lets you have unique identifiers, which are important for guaranteeing singularity, and permalinks for all the search engine friendly-ness! Now if the title of the article changes, display the updated title to the user on the page, but don't do anything to the permalink, so that your URLs never change.
By also using IDs in the URL, you know for sure that you can use a permalink multiple times, and your system will still scale correctly.
I wrote a tutorial on this which may or may not help: http://www.rappasoft.com/tutorials/14-seo-friendly-links-with-codeigniter.html

"Beautifying" a URL in Yii

I want to convert a URL which is of the format
path/to/my/app/Controller_action/id/2
to
path/to/my/app/Controller_action/id/User_corresponding_to_id_2
I have already seen this tutorial from Yii, but it isnt helping me with anything. Can anyone help me with this?
EDIT: I would also like to know if this thing is even possible in the POST scenario, ie I will only have path/to/my/app/Controller_action in the URL.
Add a getUrl method in your User model
public function getUrl()
{
return Yii::app()->createUrl('controller/action', array(
'id'=>$this->id,
'username'=>$this->username,
));
}
Add the following rule urlManager component in config/main.php
'controller/action/<username:.*?>/<id: \d+>'=>'controller/action'
And use the models url virtual attribute everywhere
dInGd0nG is on the correct track, but if I understand correctly you wish to do actions based on the actual username instead of the ID as well right?
It's not that hard in Yii. I'm assuming here for simplicity the controller is user and the action is view.
Your User controller:
public function actionView($id)
{
if (is_numeric($id))
$oUser = User::model()->findByPk($id);
else
// Luckily Yii does parameter binding, wouldn't be such a good idea otherwise :)
$oUser = User::model()->findByAttributes(array('username' => $id));
...
}
Your urlManager config:
'user/view/<id: \w+>' => 'user/view',
Or more generally:
'user/<action: \w+>/<id: \w+> => 'user/<action>',
To generate a user url in a view:
$this->createUrl('user/view', array('id' => $oUser->username));

Resources