Welcome Guest, Not a member yet? Register   Sign In
How to accomplish this > www.mysite.com/username/controller/function/id
#1

[eluser]jadebot[/eluser]
Hi guys, I am new to CI but not to php so I'm learning pretty well (thanks!)

I would like any input on how best to accomplish a clean user account experience.

To clarify, let us say I was developing an easy blog platform like wordpress in CI.
So I set up www.easyblog.com and anyone can sign up and create their online blog.
Each user would then have their own blog located at www.easyblog.com/username

But doing it like this in CI will result in CI looking for the controller "username"
If I can just get CI to let me shift everything down one slot, my problems will be solved (for now)!

Current way: www.mysite.com/controller/function/id

The way I want: www.mysite.com/username/controller/function/id

This way I can query and build everything relative to the username, and then use CI as normal after that.

I've been searching for an hour now, and I've learned about eliminating index.php, routes, and a lot of other cool things, but so far not about this!

Thanks in advance.

Edit
Just need to mention that its important that the url is structured in that exact way.
Meaning www.mysite.com/username/....
because that is what people are used to and they want to have some pride in their domain!
#2

[eluser]Leonard Yulianus[/eluser]
some of my thought

make your application goes this way
www.mysite.com/controller/function/id/id2 <-- id2 will be the username

then using routing

$route['(.*)/(.*)/(.*)/(.*)'] = "$2/$3/$4/$1";

i don't really know if this will work, just some thought (not tested though)
please tell me if this work.. or not..
#3

[eluser]ervinter[/eluser]
I am looking for this as well as I need to provided every registered member their profile page. Example, twitter.com/username
#4

[eluser]Leonard Yulianus[/eluser]
[quote author="ervinter" date="1225884310"]I am looking for this as well as I need to provided every registered member their profile page. Example, twitter.com/username[/quote]

some thought...

create a controller file (e.g user.php)
then make view function

Code:
function view($username) {
$this->db->where('username', $username);
$query = $this->db->get('users');
if ($query->num_rows()) {
// username matchs!, then do whatever with this user

} else {
// username not found! throws some error!

}
}

then add this line to application/config/routes.php
Code:
$route['(.*)'] = "user/view/$1";

note:
i haven't tested it yet, but i think it should work fine...
#5

[eluser]jadebot[/eluser]
@Psychonax

The problem with implementing your solution is that the site can only use one controller; the user controller. Once this controller is called, you can't run anything else.

---------------------------------------------------

So anyway, after testing out some stuff the whole day, I have finally got a working solution to my problem.

The problem in question is to run CI using this type of URI:
www.mysite.com/username/controller/method/id

This solution specifically addresses this problem.

Here's what I did:

Step 1: Remove index.php from your URI. Documentation for index.php removal

Step 2. Create a dynamic custom route. Routes Documention.

This is my specific instance:

Code:
/* Location: ./system/application/config/routes.php */

/* ------- Edits to enable www.mysite.com/username/controller/method/id  ---------  */
    
  //break URI into segments
    $parts = explode("/", $_SERVER['REQUEST_URI']);
    
  //delete any empty segments (allows for inclusion/exlusion of trailing slash)
    foreach ($parts as $key => $value) {
        if (is_null($value) || $value=="") {
            unset($parts[$key]);
        }
    }
    
    $site_name = $parts[1];

  //count number of useful segments being passed in URI
  //Subtract 1 from count since  $parts[1] is our username || For reference, $parts[0]  will always be deleted since it will always be empty
    $count = count($parts)-1;

  //build dynamic route. We need to execute at least once to catch and route all URI's
    do{
        $build_route .= ':any/';
        $count--;
    }
    while ($count > 0);
    $build_route = substr($build_route,0,-1);

  //build the URI to route to
    foreach($parts as $key => $part){
        if($key > 1){
            $build_url .= "{$part}/";
        }
    }
    $build_url = substr($build_url,0,-1);

  //generate finished route
    $route["$build_route"] = "$build_url";
    
  // *Troubleshoot*
    //print_r($parts);
    //echo '<br>$route["'.$build_route.'"] = "'.$build_url.'";';

Please note this is very raw code and I'm sure there is a more elegant way. I am fairly certain all this can be done with a couple lines of regular expressions but I know very little about regex so if anyone can improve this, I would appreciate it.

Step 3. Create a new hook. Hook Documentation

Code:
/* Location: ./system/application/config/hooks.php */
$hook['pre_system'] = array(
                                'class'    => '',
                                'function' => 'get_user_db',
                                'filename' => 'get_user_db.php',
                                'filepath' => 'hooks',
                                );

And here is the function I'm calling ---

Code:
/* Location: ./system/application/hooks/get_user_db.php */

function get_user_db()
{
    // get user database configuration from a master database you set up to store user credentials
    
    $host = "";
    $user = "user";
    $pass = "pass";
    $db = "database";

    $parts = explode("/", $_SERVER['REQUEST_URI']);
    $site_name = $parts[1];
    
    $connection = mysql_connect($host, $user, $pass) or die ("Unable to connect!");
    mysql_select_db($db) or die ("Unable to select master database!");

    $query = " SELECT * FROM users_db WHERE site='{$site_name}'";
    $result = mysql_query($query) or die ("Error in query: $query. ".mysql_error());
    if (mysql_num_rows($result) > 0) {
        while($row = mysql_fetch_array($result)) {        
            $username = $row['username'];
            $password = $row['password'];
            $database = $row['database'];        
        }
    }
    else{
        /*
          if no records found for user you must specify default user
          redirect to home will not work because this function will get called again and run a never ending loop
          You also can not specify a default path in config other than root because CI will append your default path to every
          link your generate. This will mess up the www.site.com/username/controller/method/id  configuration.
        */
        header('Location: /jade');
        die();
    }        
    mysql_close();
        
    /* please see notes following to explain what im doing here ... */    
    // Grab the default file
    $file_path = APPPATH.'extras/base_db.php';
    $file = file_get_contents($file_path);

    // Change the values
    $file = str_replace(array('__DB_USERNAME__', '__DB_PASSWORD__','__DB_DATABASE__'), array($username, $password, $database), $file);

    // Put it in the proper spot
    $config_path = APPPATH.'config/database.php';
    file_put_contents($config_path, $file);

    /* ------------------------ we are now working in user-specific database -------------------- */
}

The file rewrite at the bottom was taken from member: inparo's advice found here. This works on my dev platform, but since it continually rewrites the same file, I wonder ifit works in a multi-user production environment? Help on this would also be appreciated!

These steps will see to it that the user database of your choice is dynamically loaded based on the "username" segment in your URLS.

I should note that using this will force your application to always require a username. So you have to run your platform from within a users database as well.
In other words there will be no more www.mysite.com , instead you will need to run off of www.mysite.com/mysite where your the username "mysite" is an instance of your application that happens to run the root application.

Hopefully that makes sense! Please advise! Thanks.
#6

[eluser]Vince Stross[/eluser]
Have you come across the "_remap" function? You can use this in a controller to achieve what you want.

You would basically have a single "controller" controller that is also your default Route. Let's call it "home" and the first function in this controller is as follows:

Code:
function _remap(){
  ... you would use the $this->uri->... methods to read the first segment (user) and then the subsequent controller/method specifier.
}

Based on what is passed in segment1 (user name) you can do what you need to do with the user name. Then, segment 2 would be your controller and segment 3 would be your method and so on. You could basically do something like check the username, then do a "header('Location: /'.segment2.'/'.segment3.'/')" function call.

There are a LOT of cool methods in the URI class, including the "uri_to_assoc" method which I use to turn my URI into an array I can play with and the "assoc_to_uri" method which allows me to build a URI from an array. brilliant!

I can write some code for you if it would make it easier to understand, but this would be the easiest way to handle what your doing. No hooks or custom routes, etc.

EDIT: You would still need to have a route that sends everything to the "home" controller. But other than that... (I'm testing this at the moment because I really like this method and you caught me at a moment when I am starting a new app for a customer.)
#7

[eluser]Leonard Yulianus[/eluser]
my solution does not limited to only one controller...

you can always using a simple regex in routing to overcome it...
#8

[eluser]jadebot[/eluser]
@ishmael

Your solution is very elegant, just what I was looking for... but after testing it, there is a major problem:

using a header(location: "controller/method") redirect will not work in the _remap() function because "controller/method" will reroute to "home" controller again! This will carry on in an infinite loop. I have illustrated the problem based on workflow.

Workflow for your method:

1. Main URI request is sent ... www.mysite.com/username/controller/method
2. Router reroutes request to home controller
3. Home controller fetches username, does work, and redirects to "any other URI"
4. URI is picked up by router; sends to home controller!


Workflow for my method:

1. Main URI request is sent ... www.mysite.com/username/controller/method
2. Pre_system hook is triggered and calls function get_user_db()
3. get_user_db fetches username from URI, does work, and then completes.
4. Main URI is picked up by router where username is dismissed and routes to appropriate controller/method (fetched from URI)

I would love to use your method as its so much cleaner, but it seems that these steps need to be executed in a specific order.

Thanks for your help. Any thoughts on improving your method? If there is a way to call a controller from within a controller, that might work, else we are stuck.

@Psychonax

Please do tell how more than one can controller can be used from within another controller!?
#9

[eluser]Vince Stross[/eluser]
jadebot: Your solution is very nice. I've had to abandon my search for the "more elegant" solution because of my deadline. I spent many hours yesterday/night playing with this and I just KNOW the solution is alluding me. Your solution is definitely equally elegant in that it actually works!

I'll keep this on my "thinkpad" for a little while and see if a more simple solution materializes. For now, I think you should go with your own mojo though!
#10

[eluser]Leonard Yulianus[/eluser]
@jadebot

i don't mean to use controller within controller...

just set routing rules in application/config/routes.php
some simple regex will do the job, imagine this:

route everything to user controller, except call to some controller (e.g about, home, etc)




Theme © iAndrew 2016 - Forum software by © MyBB