Dynamic SEO Friendly URLs in Yii - content-management-system

I want to use custom dynamic seo friendly urls in yii.
I read all kind of articles and they all say the same thing.
This is what i managed to find so far, and it does not suit my needs:
'urlManager'=>array(
'urlFormat'=>'path',
'rules'=>array(
'<controller:\w+>/<id:\d+>'=>'<controller>/view',
'<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>',
'<controller:\w+>/<action:\w+>'=>'<controller>/<action>',
or
array(
'<_c:(post|comment)>/<id:\d+>/<_a:(create|update|delete)>'=>'<_c>/<_a>',
'<_c:(post|comment)>/<id:\d+>'=>'<_c>/view',
'<_c:(post|comment)>s/*'=>'<_c>/list',
)
I dont need urls like: domain.com/a/b/c/d
I need: domain.com/here-goes-the-article-title-ACTION-ID
I need a expression that can identify the article title.
This is how one of my urls looks like: http://www.linkbook.ro/concurs-castiga-o-invitatie-de-trei-zile-de-festival-la-bestfest-2012-detailsU-2-882.html
where concurs-castiga-o-invitatie-de-trei-zile-de-festival-la-bestfest-2012 is the article title
detailsU is the action
2 is the database id
882 is the article id

Let me formulate my comment as an answer. You still have to implement some stuff yourself, but this should get you started:
class MyRule extends CBaseUrlRule
{
public function parseUrl($oManager, $oRequest, $sPathInfo, $sRawPathInfo)
{
// Extract database Id and article Id from $sPathInfo and perhaps put it in $_REQUEST
if ("url isn't SEO thingy")
return FALSE:
return 'articles/index';
}
public function createUrl($oManager, $sRoute, $aParameters, $sAmpersand)
{
if ("i have an SEO item to show")
return "/however you want to assemble your URL";
return FALSE;
}
}
The above example assumes that you route everything through the articles controller (action index).
Adding to the config is a matter of adding the following to your rules:
'urlManager'=>array(
'urlFormat'=>'path',
'rules'=>array(
array('class' => 'MyRule'),
'<controller:\w+>/<id:\d+>'=>'<controller>/view',
'<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>',
'<controller:\w+>/<action:\w+>'=>'<controller>/<action>',

Related

How to make morre readable URIs using unique-id-plus-redundant-information (UPRI) using PHP with Laravel

I would like to know the best (and most consistent) way to add redundant bits to my restful uris so that they are more readable while remaining unchanging when some things such as username change.
I read of the concept at this excellent blog post and it is something like what stack overflow does /users/3836923/inkalimeva where the last segment of the URI is redundant and may change but makes the URI more readable and SEO friendly.
Currently I am using Laravel's Route::resource() but that creates routes with only the id segment.
You can use eloquent-sluggable to create slugs for your users. That way the slug will change when they update their username. You can also simply call their username in the url method, though this will result in uglier urls.
This method still requires that you drop Route::resource() and write your routes explicitly.
Here is the code, tested and working:
ROUTES.PHP (don't mind the route details)
Route::get('route-name/{id}/{slugOrUsernameAsYouPlease}', [
'as' => 'admin-confirm-detach-admin',
'uses' => 'AdminController#confirmDetachAdmin'
]);
IN YOUR VIEW
Click me!
OR
Click me!
URL RESULT (My users name here is Fnup. Just for testing)
With Username: http://website.local/route-name/8/Fnup
With Slug: http://website.local/route-name/8/fnup
A quick final note
I just changed fnup's username to fnupper and here is the result:
http://website.local/route-name/8/Fnupper
However the slug didn't change automatically. You have to add that code yourself to the user update method. Otherwise the slug stays as what it was the first time the resource was made. Here is my code when using eloquent-sluggable
public function update(UpdateUserRequest $request)
{
$user = \Auth::user();
$user->name = $request->name;
$user->email = $request->email;
$user->resluggify();
$user->save();
session()->flash('message', 'Din profil er opdateret!');
return redirect()->route('user-show');
}
Which result in: http://website.local/route-name/8/fnupper
New edit per request: Controller method example
Here is my confirmDetachAdmin() method in AdminController.php. Just to clarify, the methods job is to show a "confirm" view before modifying a users status. Just like edit/update & create/store, I made up confirm to accompany destroy (since I'd like a javascript free confirmation option should javascript be disabled).
public function confirmAttachAdmin($id)
{
$user = User::findOrFail($id);
/* Prevent error if user already has role */
if ( $user->hasRole('admin')) {
return redirect()->back();
}
return view('admin.confirmAttachAdmin', compact('user'));
}
You can add your slug/username as a second parameter if you want to, but I don't see a reason, as you can access it from $user when you find them by id.
As opposed to #MartinJH's answer, I don't think you should store your slugs in database if you don't rely only on them in your URIs. A simple link() method on your model, and an explicit route is enough.
App\User
class User extends \Illuminate\Database\Eloquent\Model {
public function link()
{
return route('user-profile', [ $this->id, Str::slug($this->username) ]);
}
}
routes.php
Route::get('{id}/{username}', [ 'as' => 'user-profile', 'uses' => 'UserController#profile' ])
->where('id', '\d+')
->where('username', '[a-zA-Z0-9\-\_]+');
App\Http\Controllers\UserController
...
public function profile($id, $username)
{
$user = \App\User::findOrFail($id);
return view('profile')->with('user', $user);
}
...

ASP.NET Web API REST Querystring - How does a client know available parameters and options?

When exposing querystring parameters using GET I have the following base URL:
https://school.service.com/api/students
This will return the first 25 students.
What if I want to return a list of students based on ONE of the following criteria:
* have accepted a job
* have received a job offer
* have no job offers
The three above choices are essentially an enum.
Therefore, the query request for students who have no job offers I assume would look like:
https://school.service.com/api/students?jobOfferStatus=3
However, I'm wondering if jobOfferStatus=3 is the proper way to handle this. If so, how would I publish/provide to the clients a list of available options for that jobOfferStatus query parameter? What about other possible query parameters and their valid options? We'll have many possible query parameters like this.
I'd love to see an example of how this should be done properly. What are the best practices?
There are two main options: documenting it, or making it discoverable. A lot of APIs have documentation where they list all of the resources and parameters for reference. Otherwise, the client won't know.
You could also make it discoverable in some way by including the options in a response. For conventions on this, search for HATEOAS if you haven't already. (I'm not really knowledgeable enough about HATEOAS myself to make a suggestion.)
I will mention that "3" is not a very meaningful value for jobOfferStatus, and there's no need for the client to know that number. You can make it anything you want -- jobOfferStatus=none or even jobOffer=none. Your controller can do the work of matching that value to your enumeration. Try to design your interface to be intuitive for developers (and, of course, write good documentation).
To handle multiple query parameters, you can use optional parameters in your function:
public HttpResponseMessage GetStudents(string jobOffer = "",
string other1 = "",
string other2 = "")
{
if (jobOffer == "accepted" && other2 == "whatever") {
// return a response
}
else {
// return a different response
}
}
When the client uses parameters by those names, you can tailor your response appropriately.
You have some options to do this, let's try to help:
1) Configure a generic route to asp.net web api knows how to solve another action's name different from Get to a get method, on the App_Start\WebConfigApi.cs class, try to add this:
config.Routes.MapHttpRoute("DefaultApiWithActionAndId",
"api/{controller}/{action}/{id}",
new { id = RouteParameter.Optional });
Using it, you can have diferent methods on the api controller:
// request: get
// url: api/Students/GetStudents
public HttpResponseMessage GetStudents()
{
return Request.CreateResponse(...);
}
// request: get
// url: api/Students/GetStudentsWithJobOffer
public HttpResponseMessage GetStudentsWithJobOffer()
{
return Request.CreateResponse(...);
}
// request: get
// url: api/Students/GetStudentsAcceptedJob
public HttpResponseMessage GetStudentsAcceptedJob()
{
return Request.CreateResponse(...);
}
2) Use a simple parameter on the Get method:
// request: get
// url: api/Students?jobOfferStatus=1
public HttpResponseMessage GetStudents(int jobOfferStatus)
{
// use jobOfferStatus parameter to fill some list
return Request.CreateResponse(...);
}
3) Use a simple method with a parameter named id, to get a default friendly url by asp.net mvc web api.
// request: get
// url: api/Students/1
public HttpResponseMessage GetStudents(int id)
{
// use the id parameter to fill some list
return Request.CreateResponse(...);
}

CakePHP: RESTful prefixed action

I have troubles to do REST application in CakePHP, requesting GET /admin/quote_authors/1.json sends me to the 'view' action, not 'admin_view'.
route.php:
Router::parseExtensions('json');
Router::mapResources(array(':controller'), array('prefix' => '/admin/'));
QuoteAuthorsController.php:
public $components = array('RequestHandler');
public function admin_view($id) {
var_dump('admin view');
}
public function view($id) {
var_dump('view');
}
Thanks.
Answering because I can't comment.
You seem to be missing the action part of the request /admin/quote_authors/view/1.json
So for other request it would be like /admin/:controller/:action/:params in general.
And, of course, like thaJeztah said, remove the slashes of the prefix (that's why it's giving you that error, it's considering the parameter "1" as the action it has to execute)

Does Luracast Restler support multi-part URIs?

I've recently started a project using Luracast Restler. It seems a very simple and effective way to set up a REST API. With very little code, I was able to provide CRUD services for my Category and Product resources.
My GET methods look like this:
class Categories
{
function get($id=NULL) {
if (isset($id))
{
// return category details for $id.
}
else
{
// return all categories.
}
}
}
class Products
{
function get($id=NULL) {
if (isset($id))
{
// return product details for $id.
}
else
{
// return all products.
}
}
}
Clients can get the details of the "books" category using:
http:api/categories/books
or all categories using:
http:api/categories
Same for products. One product:
http:api/products/123
All products:
http:api/products
So far so good.
Now I want to progress to something slightly more involved. I want to give my clients access to the products in a category.
I want my URI to be:
http:api/categories//products
E.g.
http:api/categories/books/products
and from there, I want to offer:
http:api/categories//products/
E.g.
http:api/categories/books/products/123
This gives my client the ability to transfer from one resource to another using a progressive series of links, which I see as a core principle of REST.
But I can't see a way of achieving this with Restler. I've seen some mention of JavaDoc comments being used to specify URI mapping, so I tried this:
class Products
{
/**
* url GET /categories/:catId/products/:prodId
*/
function get($catId=NULL, $prodId=NULL) {
// Get product($prodId) of category($catId)
}
}
But this doesn’t work. Restler doesn’t seem to take any information from the comment; it implicitly creates the URI route based on class name and function name.
Can anyone help? Am I missing something? Any advice would be much appreciated.
Everything is fine in the example above and what you are trying to achieve except one simple mistake that stopped it from working!
Your PHPDoc comment is missing #
Change your code as follows
<?php
class Products
{
/**
* #url GET /categories/:catId/products/:prodId
*/
function get($catId=NULL, $prodId=NULL) {
// Get product($prodId) of category($catId)
}
}
Also take a look at the related question
How do you organize Luracast Restler classes to create related route endpoints?

How do I map Grails Searchable plugin across more than 2 domain objects?

I'm using the Searchable plugin in my Grails application, but am having trouble getting it to map across more than 2 domain objects while returning valid search results. I've looked through the Searchable plugin documentation, but cannot find the answer to my question. Here's a very basic example of the domains I have:
class Article {
static hasMany = [tags: ArticleTag]
String title
String body
}
class ArticleTag {
Article article
Tag tag
}
class Tag {
String name
}
Ultimately what I'm looking to do is be able to find articles by searching their titles, body and associated tags. The titles and tags would be boosted as well.
What's the proper way to map these classes to meet the desired results?
There is probably another approach, but this is the simple approach I used in my application. I added a method to the domain object to get all of string values from the tags and add them to the index with the Article object.
This allows me to just search the Article domain object and get everything I need
class Article {
static searchable = {
// don't add id and version to index
except = ['id', 'version']
title boost: 2.0
tag boost:2.0
// make the name in the index be tag
tagValues name: 'tag'
}
static hasMany = [tags: ArticleTag]
String title
String body
// do not store tagValues in database
static transients = ['tagValues']
// create a string value holding all of the tags
// this will store them with the Article object in the index
String getTagValues() {
tags.collect {it.tag}.join(", ")
}
}