CodeIgniter - hidden controller - php

I'm building a CMS based on CodeIgniter. It stores "views" and its data in a database and gathers the proper one when needed.
As you might have guessed - I can't generate a physical controller and matching view for each pages.
I've figured that routes would come in quite handy, since I'd prefer not to have to use a controller that's visible in the URL. Poorly explained: I'm looking for a way to reassign all requests that doesn't end up on a physically existing controller to a custom one - without it appearing in the URL. This controller would, of course, handle 404-errors and such.
Bad: .com/handler/actual-view/) Good: (.com/actual-view/) (no actual-view controller exists, or it'd be shown instead)
I've added a 404_override route which points to handler/. Now, I'm only looking for a way to find out the requested view (i.e in .com/actual-view actual-view is what I'm looking for).
I've tried
$route['404_override/(:any)'] = 'handler/$1';
and similar, which will remove the 404-override completely.

You would be better of extending the base Router or Controller.
By doing so you allow your application to be flexible and still conform to how CI works.

you need to define all of your valid routes in your route.php config file, and then at the last line,
$routes["(:any)"] = "specific controller path";
if I should give an example:
$route['u/(:any)/account'] = "user_profile/account/$1";
$route['u/(:any)/settings'] = "user_profile/settings/$1";
$route['u/(:any)/messages'] = "user_profile/messages/$1";
$route['u/(:any)'] = "user_profile/index/$1";
as seen here, I'm diverting all the urls to user profile, after the first three one couldn't catch it.

My solution, after some guidance from CodeIgniters awesome forum and StackOverflow's lovely members, became to route all 404 errors to my custom controller where I ensure that it's a real 404 (no views OR controllers). Later in the controller, I gather the rest of the info I need from my database URI string:
//Route
$route['404_override'] = 'start/handler';
//Controller
function handler($path = false) {
//Gather the URI from the URL-helper
$uri_string = uri_string();
//Ensure we only get the desired view and not its arguments
if(stripos($uri_string, "/") !== false) {
//Split and gather the first piece
$pieces = explode("/", $uri_string);
$desired_view = $pieces[0];
} else {
$desired_view = $uri_string;
}
//Check if there's any view under this alias
if($this->site->is_custom_view($desired_view)) {
//There is: ensure that the view has something to show
if(!$this->site->view_has_data($desired_view)) {
//No data to show, throw an error message
show_custom_error('no_view_data');
} else {
//Found the views data: show it
}
} else {
//No view to show, lets go with 404
show_custom_404();
}
}

Related

Laravel routing - two url addresses with similar names

I'm doing forum with Laravel.
I decides to have routes like this:
Route::get('/{topicName}', 'ForumController#showTopic');
Route::get('/{postSubject}', 'ForumController#showPost');
I have also another routes, but this two are on the bottom, because, when I write sth in URL (and laravel doesn't find passing address) then everything fall into this routes (especially the first one). I don't know how to programm this to work this two last routes.
When someone add my Topic name, then he's going on the site:
http:/forum/php
or
http:/forum/javaScript
Then user see all posts to this Topic. But when user want to see one specific post, then I want to be in url like this:
http:/forum/post_subject_name
And now user can see specific post.
How to do this, because now everything fall to my first controller - ForumController#showTopic'). Is this possible?
You can't, both those routes have the same requirement, if they are at the bottom of your routes, the topicName one will always take priority.
You should have routes such as
Route::get('/topics/{topicName}', 'ForumController#showTopic');
Route::get('/posts/{postSubject}', 'ForumController#showPost');
That way you can distinguish between them
Problem is that both routes consist of one parameter and nothing else. How is it supposed to know if the given parameter is a topic name or a post subject?
However, what you could do is having one route and do the rest in the controller method:
Route::get('/{topicOrPost}', 'ForumController#showTopicOrPost');
public function showTopicOrPost($topicOrPost)
{
$topic = Topic::where('name', $topicOrPost)->first();
if ($topic !== null) {
// show the topic
} else {
$post = Post::where('subject', $topicOrPost)->first();
if ($post !== null) {
// show the post
} else {
// neither topic or post found
}
}
}
But then, of course, you'd have to ensure that there aren't topic and post with the same name/subject.
in my opinion you could use pattern in route
Regular Expression Constraints
some example :
// This is what you might have right now
Route::get('users/{id}', 'UserController#getProfile')->where('id', '[\d+]+');
Route::get('products/{id}', 'ProductController#getProfile')->where('id', '[\d+]+');
Route::get('articles/{slug}', 'ArticleController#getFull')->where('slug', '[a-z0-9-]+');
Route::get('faq/{slug}', 'FaqController#getQuestion')->where('slug', '[a-z0-9-]+');
// and many more, now imagine you'll have to change the rule
// Instead, you could have a handy list of patterns and reuse them everywhere:
// Patterns
Route::pattern('id', '\d+');
Route::pattern('hash', '[a-z0-9]+');
Route::pattern('hex', '[a-f0-9]+');
Route::pattern('uuid', '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}');
Route::pattern('base', '[a-zA-Z0-9]+');
Route::pattern('slug', '[a-z0-9-]+');
Route
::pattern('username', '[a-z0-9_-]{3,16}');
and for you I think you could use something like this:
Route::get('/{any}','SearchController#showPost')->where('any','^[post_]+$');
Route::get('/{any}','SearchController#showTopic');
but you have to use ShowTopic route after showpost

Redirect specific routes to page if they don't exist with laravel 4

I have a route that needs to be redirected to another page if the data they're pulling doesn't exist. The route is:
Route::get('{link}/{data}', 'LinkController#getLink');
Where {link} and {data} are model bound with:
Route::model('link', 'Link');
Route::model('data', 'Data');
As is, when the data for this link doesn't exist it 404's, and if it does exist, it's taken to the page as it should. What I would like to do is redirect to another page if the link would otherwise 404. I've found suggestions on how to do this globally, but I only want it to happen on this one route.
Any ideas?
// Link Controller
public function getLink($linkId, $dataId)
{
if ( is_null($link) or is_null($data) ) {
return Redirect::to('some/path');
}
}
If either of the passed models are null when it hits your controller method, just redirect them. As for your /{link} route that you refer to but don't show code for, do something similar in whatever closure/controller you handle that in.
Get rid of the model binding - you've left the cookie cutter realm.
Route::get('{link}/{data?}', 'LinkController#getLink');
// note I made the data ^ parameter optional
// not sure if you want to use it like this but it's worth pointing out
Do all of the model checking in the controller, something like this:
public function getLink($linkId, $dataId)
{
$link = Link::find($linkId);
$data = Data::find($dataId);
if(is_null($link)){
throw new NotFoundHttpException;// 404
}
elseif(is_null($data)){
return Redirect::to('some/view');// redirect
}
// You could also check for both not found and handle that case differently as well.
}
It's hard to tell from your comments exactly how you'd like to treat missing link and/or data records, but I'm sure you can figure that out logically. The point of this answer is that you don't need to use Laravel's model binding since you can do it yourself: find the record(s) else redirect or 404.

CakePHP: how to do sub-actions with sub-views

Apologies if I'm using the wrong terminology to describe what I'm trying to do...
I have a model/controller called Report which users can view like so:
example.com/reports/view/123
Each report hasAndBelongsToMany File objects. I need to make those files accessible like so:
example.com/reports/view/123/file/456
Or
example.com/reports/view/123/456
^ ^
| |
report file
I'm intentionally NOT creating a separate action for files (example.com/files/view...) because access to the file is relative to the report.
What is the correct way to do this in CakePHP?
My first guess is to add logic inside of ReportsController::view that checks for the existence of the second parameter (file) and manually render() a different view (for the file) conditionally. But I'm not sure if this is "the CakePHP way".
You are in the right path, modify your action to accept an optional parameter.
public function view($file = null) {
$somethingElse = null;
if (isset($file)) {
//your logic
$somethingElse = $this->Foo->bar();
}
$this->set(compact('somethingElse'));
}
Regarding to the view, I don't know your requirements, but I think you don't need to create a different view, you can either put a conditional in your view to show something, or (my favourite approach) create an element that will be displayed only if $somethingElse contains something. That is:
//View code
if (!empty($somethingElse)) {
echo $this->element('yourAwesomeElement', compact('somethingElse'))
}
Then in yourAwesomeElement
foreach ($somethingElse as $something) {
echo $something;
}
The good thing is that your element will be reusable for future views that could need this.

Embedding CakePHP in existing page

I've volunteered to create some db app, and I told those guys that it will be very easy, since I wanted to use CakePHP. Sadly after some time they told me they want it inside their already existing web, which is ancient highly customized PHPNuke.
So what I want is to generate just content of one <div> inside an already existing page with CakePHP. I looked up on the internet, but I didn't find what I was looking for. I'm rather a user of the framework, not developer, so I don't know much about the backend and how MVC frameworks are working inside (and this is my first try with CakePHP, since I'm Rails guy).
What I did so far is disabling mod_rewrite for Cake. Inside PHPNuke module I included Cake's index.php and rendering views with an empty layout. This somehow works, but the thing is how to form URLs. I got it working by now with
http://localhost/modules.php/posts?op=modload&name=xxxxx&file=index&do=xxxxx
but with this all links to CSS and images on PHPNuke site are broken.
Is there any way to use something like
http://localhost/modules.php?op=modload&name=xxxxx&file=index&do=xxxxx&CakePHP=/posts/bla/bla
or any other way that could do the job? I really don't want to change anything in existing PHPNuke app.
Thank you very much
Well, if you don't understand how CakePHP works you'll have trouble doing what you want, since it would mean putting hacks into the CakePHP core files to bypass the default routing. This basically means that you would be re-working the way CakePHP works, so you can forget about ever updating to a newer CakePHP version, and maintenance would be hell.
If you want to modify the system, but keep PHP-Nuke, I'd advise against jamming CakePHP in there, since that would open up too many problems to be able to predict beforehand.
I think your options are as follows:
Learn how PHP-Nuke works so you can modify it
Use regular php for the pages
Either of those are easier by orders of magnitude compared to what you wanted to do.
So to sum up solution I found, if someone will be looking for something similar. Problem solved by using two custom route classes ( http://manual.cakephp.neoboots.com/2.0/en/development/routing.html#custom-route-classes )
class CustomParserRoute extends CakeRoute {
function parse($url) {
if (parent::parse($url) != false) //if default parser has the match continue
{
// call to Router class to do the routing for new url string again,
// if &cakePHP= is in query string, use this, or use default
if ($_GET['cakePHP']) {
$params = Router::parse($_GET['cakePHP']);
} else {
$params = Router::parse("/my_controller");
}
return $params;
}
return false;
}
}
class CustomMatcherRoute extends CakeRoute {
// cusotm mathc function, that generates url string.
// If this route matches the url array, url string is generated
// with usual way and in the end added to url query used by PHPNuke
function match($url) {
$result_url = parent::match($url);
if($result_url!= false) {
$newurl = function_to_generate_custom_query()."&cakePHP=".$result_url;
return $newurl;
} else {
return $result_url;
}
}
}
And then simple configuration in routes php
App::import('Lib', 'CustomParserRoute');
App::import('Lib', 'CustomMatcherRoute');
// entry point to custom routing, if route starts with modules.php it matches
// the url and CustomParserRoute::parse class is called
// and route from query string is processed
Router::connect('/modules.php', array('controller' => 'my_controller'), array('routeClass' => 'CustomParserRoute'));
// actual routes used by cakephp app, usual routes that need to use
// CustomMatcherRoute classe, so when new url is generated, it is modified
// to be handled later by route defined above.
Router::connect('/my_controller/:action/*', array('controller' => 'my_controller'), array('routeClass' => 'CustomMatcherRoute'));

codeigniter routing

I am currently working on CMS for a client, and I am going to be using Codeigniter to build on top of, it is only a quick project so I am not looking for a robust solution.
To create pages, I am getting to save the page details and the pull the correct page, based on the slug matching the slug in the mysql table.
My question is however, for this to work, I have to pass this slug from the URL the controller then to the model, this means that I also have too have the controller in the URL which I do not want is it possible to remove the controller from the URL with routes?
so
/page/our-story
becomes
/our-story
Is this possible
I would recommend to do it this way.
Let's say that you have : controller "page" / Method "show"
$route['page/show/:any'] = "$1";
or method is index which I don't recommend, and if you have something like news, add the following.
$route['news/show/:any'] = "news/$1";
That's it.
Yes, certainly. I just recently built a Codeigniter driven CMS myself. The whole purpose of routes is to change how your urls look and function. It helps you break away from the controller/function/argument/argument paradigm and lets you choose how you want your url's to look like.
Create a pages controller in your controllers directory
Place a _remap function inside of it to catch all requests to the controller
If you are using the latest version of CI 2.0 from Bitbucket, then in your routes.php file you can put this at the bottom of the file: $routes['404_override'] = "pages"; and then all calls to controllers that don't exist will be sent to your controller and you then can check for the presence of URL chunks. You should also make pages your default controller value as well.
See my answer for a similar question here from a few months back for example code and working code that I use in my Codeigniter CMS.
Here's the code I used in a recent project to achieve this. I borrowed it from somewhere; can't remember where.
function _remap($method)
{
$param_offset = 2;
// Default to index
if ( ! method_exists($this, $method))
{
// We need one more param
$param_offset = 1;
$method = 'index';
}
// Since all we get is $method, load up everything else in the URI
$params = array_slice($this->uri->rsegment_array(), $param_offset);
// Call the determined method with all params
call_user_func_array(array($this, $method), $params);
}
Then, my index function is where you would put your page function.

Categories