Welcome Guest, Not a member yet? Register   Sign In
Route using database information
#1

[eluser]WanWizard[/eluser]
Hello from this Newbie.

I'm planning to rewrite our current web framework (which has been around since the PHP3 days, and in dire need of a makeover), and the plan is to use this fantastic framework as the foundation.

While writing down the new architecture and design (yes, I'm old ;-)), I have stumbled upon an issue that I don't quite know how to address.

Our current system uses a 'content-tree', which is basically the URI structure of the site, and is stored in a database. The system administrator can map any leaf of the tree (a specific URI) to a function, either static content, a core module (p.e. user administration) or a custom application (p.e. products).

This means in CI terms (if I understand it correctly) that I need to route based on database contents. From what I gather I can do that in:
- /config/routes.php, generate a custom $route['(.*)'] = for every URI requested
issue: no database functionality here, no URI functionality here
- /system/application/libraries/MY_Router.php, and rewrite the _validate_request() function
I do have URI functionality here, but still no database access
- /system/application/controller/dispatcher.php, and use a $route['(.*)'] = "dispatcher";
I have all CI functionality here, but basically I need to duplicate router and CI core code here to be able to load the correct controller that is the result of the content-tree lookup. Not best practice either.

Anyone any thoughts on how to approach this?
#2

[eluser]taewoo[/eluser]
I am not certain about this... but what if you extended the router core and have that access the DB
#3

[eluser]Colin Williams[/eluser]
If you trace through CI bootstrapping, you quickly realize that there's no way to access the DB in the Router class. Well, you can, but you inevitably end up with two database connections. The only way around it is to rewrite the bootstrapping process so that the database libraries are available right away (and this involves changing a lot of processes down the line, like, you would have to redo how the db class gets referenced to the CI object so your can do $this->db still).

It's not impossible, but you can't do it without significantly changing core, or living with two DB connections per request.

What I've done is have the config/routes.php file dynamically written to based on values in the database. This seems to work best without modifying CI extensively.
#4

[eluser]WanWizard[/eluser]
I don't intend to make any changes to the core code, that would potentially mean complex merge operations when CI gets updated. I'd like to stay away from that.

I quite like the idea of generating a new config/route.php after an update of the content tree, although I have to see how that works out, with hundreds of one-to-one mappings in $route (and it requires the webserver user to have write access to a file in the docroot, which is not really best practice as well).

I'm not to troubled with two database connections, I tend to use persistent connections anyway. I was sort of hoping that I could utilize some of the CI power, instead of coding everything. That means lots of code, and/or lose database portability (because I can't use CI's abstraction layer).

Taking that into account, probably the best option is to route all requests through my own front controller. That will have access to all CI classes, which makes the database part a lot easier, but it means I have to duplicate the code that loads the final controller and calls the appropriate method, from codeigniter.php.

hmmm... decisions, decisions...
#5

[eluser]taewoo[/eluser]
Does your DB change often? If not, maybe you can do a cron job that'll write out the routes.php file every X seconds or so. Hack but it'll work. Not to mention, u won't have to change the core...
#6

[eluser]Colin Williams[/eluser]
I think cron is a good way to go if you're weary about the webserver user writing to that file.

Also, going through your own front controller is seems to be a decent solution. I was going to go that route once...
#7

[eluser]WanWizard[/eluser]
Sometimes they do.

It's a web framework that others use. Some have made it into a community website or a corporate intranet site, were a fixed set of modules (controllers in CI speak) are used, and hardly no static (HTML) pages are added or updated. Other sites mainly use static pages, but those change constantly (news sites, blogs, that kind of sites).

Also, not everyone (p.e. on hosted webservers) has access to cron.

As you said, it's a hack. And i'll like a more structural solution. Wink
#8

[eluser]taewoo[/eluser]
hack - in a sense that it's not a CI solution.
no access to cron - CI forum people are not gods.. They can't solve ALL your needs. Wink
#9

[eluser]Aken[/eluser]
I have a similar solution set up to mine. For my site's news posts, I specify the local URL that I want. Then if the users goes to .com/news/new-post, it queries the DB for the URL "new-post". If it is found, a page is displayed. If it's not found, a 404 error is given.

So in essence, you could set up a single route so that any URL specified for your entire site, or a certain section, goes to a specific controller, and then that controller queries to see if the page actually exists.
#10

[eluser]Colin Williams[/eluser]
Here's the class I got started on... maybe 50% of the way to being fully robust. It writes to config/db_routes.php, which you would then require or include from config/routes.php

Code:
<?php

class Alias {
    
    var $aliases = array();
    var $table = 'alias';
    var $ci;
    
    function Alias($params = array())
    {
        $this->ci =& get_instance();
        $this->set_aliases();
    }
    
    function set_aliases()
    {
        $this->ci->load->database();
        $query = $this->ci->db->get($this->table);
        if ($query->num_rows() > 0)
        {
            foreach ($query->result() as $alias)
            {
                $this->aliases[$alias->alias] = $alias->path;
            }
        }
    }
    
    function _build_routes()
    {
        $file_contents = '';
        if (count($this->aliases))
        {
            foreach($this->aliases as $route => $dest)
            {
                // Escape double quotes that aren't yet escaped
                // addcslashes might actually work here too... not sure
                $route = preg_replace('/(?<!\\\)\"/', '\"', $route);
                $dest = preg_replace('/(?<!\\\)\"/', '\"', $dest);
                $file_contents .= '$route["'. $route .'"] = "'. $dest .'";'."\n";
            }
        }
        $dbr = fopen(APPPATH .'config/db_routes'. EXT, 'w');
        fwrite($dbr, '&lt;?php  if (!defined("BASEPATH")) exit("No direct script access allowed");');
        fwrite($dbr, "\n\n");
        fwrite($dbr, $file_contents);
        fclose($dbr);
    }
    
    function create($alias, $path, $if_exists = 'UPDATE')
    {
        if (is_array($alias))
        {
            foreach ($alias as $a => $p)
            {
                if (is_string($a) and is_string($p))
                {
                    $this->create($a, $p);
                }
            }
        }
        else if (is_string($alias) and is_string($path))
        {
            $new = array(
                'alias' => $this->_clean_alias($alias),
                'path' => $this->_clean_path($path),
            );
            if (isset($this->aliases[$new['alias']]) && $if_exists != 'UPDATE')
            {
                return FALSE;
            }
            else if (isset($this->aliases[$new['alias']]))
            {
                $this->ci->db->where('alias', $new['alias']);
                if ($this->ci->db->update($this->table, $new))
                {
                    $this->aliases[$new['alias']] = $new['path'];
                    return TRUE;
                }
            }
            else {
                if ($this->ci->db->insert($this->table, $new))
                {
                    $this->aliases[$new['alias']] = $new['path'];
                    return TRUE;
                }
            }
        }
    }
    
    function _clean_alias($alias)
    {
        return $this->ci->input->xss_clean($alias);
    }
    
    function _clean_path($path)
    {
        return $this->ci->input->xss_clean($path);
    }
    
}




Theme © iAndrew 2016 - Forum software by © MyBB