Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » .htaccess URL rewrite

Credits go to BAF, CGamesPlay, Matthew Leverton, Thomas Fjellstrom, and X-G for helping out!
This thread is locked; no one can reply to it. rss feed Print
 1   2   3   4 
.htaccess URL rewrite
Matthew Leverton
Supreme Loser
January 1999
avatar

Quote:

Regarding the HTML thing, I think there was a misunderstanding. I maybe just talking garbage but anyway. Allegro.cc has two sidebars and the center where the content is. Now, I am speculating, but I reckon that the sidebars are constant ie. are not tied to modules? You know what I mean? Sure, the content is tied to a module, but what about the sidebars etc.? Guess theres not much need for that anyway... Just asking.

A.cc isn't written on my complete CMS engine, but if it might look something like this if it weren't static:

1// index.tpl
2<mef:template container="three-columns">
3 
4 <html>
5 <ul>
6 <li mef:loop="n; news">
7 <h2>{n.caption</h2>
8 <div>{n.body}</div>
9 </li>
10 </ul>
11 </html>
12 
13 <leftcolumn>
14 <h2>Allegro.cc</h2>
15 <ul>
16 <li>Depot</li>
17 <li>Forums</li>
18 </ul>
19 </leftcolumn>
20 
21</mef:template>

The first layer of tags is just a way to name a block of HTML. The container file could then reference them as {html} and {leftcolumn}.

Or another approach could be:

1// index.tpl
2<mef:template container="three-columns" sidebar="Depot,Resources">
3 // blah
4</mef:template>
5 
6// three-columns.tpl
7<html>
8 
9<body>
10 
11<div id="left-column">
12 <acc:column resources="{sidebar}" /> // a custom tag written in PHP
13</div>
14 
15<div id="main">
16 {html}
17</div>
18 
19</body>
20 
21</html>

There's plenty of ways to do it. Obviously you want to keep the template files as close to pure HTML as possible. Sometimes this means creating custom tags that generate HTML that's otherwise hard to represent.

Vanneto
Member #8,643
May 2007

OK I got something figured out. Just need to put that down into code now. I have a question though, everything beyond the base directory of the domain is the Query string?

And I still don't get this line:

class News_Module extends Module implements IRequestHandler

I mean, I thought I got it but I don't. I tried to code something similar and now I know my ignorance is bigger then I thought. :P

In capitalist America bank robs you.

Matthew Leverton
Supreme Loser
January 1999
avatar

An interface is an empty class:

interface Foo
{
  public function bar();
}

class Bar implements Foo
{
}
// error - it has no function called bar

In a language like PHP, it's more of just a formality. It's just a way to make sure a class implements a set of functions.

Vanneto
Member #8,643
May 2007

OK I just got some of the basics working, pretty nifty I must say. So, do you actually have two separate methods in a module - that handle get and post requests? For example:

public function handle_get_request(httpreq $request);
public function handle_post_request(httpreq $request);

Although I do not see the benefit in this as you can just as easily handle everything as GET and then check for $_POST vars in the get handler. Maybe I can make some sort of automatic form handler for these things.

In capitalist America bank robs you.

Matthew Leverton
Supreme Loser
January 1999
avatar

I handle them separately just because there's an ideological difference: GET should be used when requesting a read-only page, and POST should be used when pushing data to a read/write page.

But technically there's little difference between them, and they could easily be handled together.

CGamesPlay
Member #2,559
July 2002
avatar

I don't like the idea of splitting GET and POST. It would seem like the biggest advantage of this method comes from being able to GET a form and then POST to the same URL, and by splitting it, you make the code more modular and readable. But I've found that much of the code is shared between the two requests (typically, the code for a POST request is just a preamble to the code for the GET request), and so you harm your readability by splitting it.

--
Tomasu: Every time you read this: hugging!

Ryan Patterson - <http://cgamesplay.com/>

Matthew Leverton
Supreme Loser
January 1999
avatar

Yes, but I handle it much differently within my CMS. I don't care what URL you submit the POST data to. It's all encapsulated within a POST wrapper request. So my POST request handler looks more like:

function handlePost($post)
{
  if ($post->namespace != 'News') return false;

  switch ($post->action)
  {
    case 'delete':
      // check permissions
      // validate fields
      // perform operation
      // redirect
    break;
  }
}

This is because I don't want the CMS to define the URLs for all the applications it hosts. In short, you make a request with the namespace/action (e.g., News.Delete) along with a redirect URL on success (if wanted).

If you don't redirect and there is no error, then the "GET" request handler would kick in.

Vanneto
Member #8,643
May 2007

OK, I got it working with a prefixed set of Modules. Now, does PHP have any INI handling functions? Yeah, I know I can write my own and quite easily. But, just to avoid the hassle... Is there?

In capitalist America bank robs you.

Matthew Leverton
Supreme Loser
January 1999
avatar

Vanneto
Member #8,643
May 2007

Great, got everything working. Except one little thing. For example, www.mysite.com/ should display the news. So, it works if the url is www.mysite.com/news/ or something but how do I do it if no params are defined?

EDIT:

Another thing, lets say you have a Statistics module. Now, this has to be called in every page. But if News is first and the param is news then News module will return true and loading of modules stops there. Confusion! :P

In capitalist America bank robs you.

Matthew Leverton
Supreme Loser
January 1999
avatar

Quote:

Great, got everything working. Except one little thing. For example, www.mysite.com/ should display the news. So, it works if the url is www.mysite.com/news/ or something but how do I do it if no params are defined?

I don't understand your problem. There's nothing stopping any module from handling any request.

Quote:

Another thing, lets say you have a Statistics module. Now, this has to be called in every page. But if News is first and the param is news then News module will return true and loading of modules stops there. Confusion!

A module could do something transparent within the handleRequest (e.g., log to a database), but still return FALSE as to say that it didn't handle the request. Just make sure the Statistics module is loaded first. I treat the INI file as first come, first serve. That's been good enough for me.

Vanneto
Member #8,643
May 2007

Quote:

I don't understand your problem. There's nothing stopping any module from handling any request.

Well I have something like this:

$request = new HTTPRequest();
$request->path = /* ... */;

$return = false;
foreach($modules as $key => $value)
{
    if($value == "active")
    {
        $module = $system->load_module($key);
        $return = $module->handle_http_request($request);
        if($return === true) break;   /* This line is the culprit I guess */
    }
}

But you already answered that question:

Quote:

A module could do something transparent within the handleRequest (e.g., log to a database), but still return FALSE as to say that it didn't handle the request. Just make sure the Statistics module is loaded first. I treat the INI file as first come, first serve. That's been good enough for me.

But that seems so, hackish, interfering with the natural flow of the PHP code is a dangerous business indeed. :P

I could implement something like an always_load option or something similar, but your solution sounds so much simpler! ;D

In capitalist America bank robs you.

Matthew Leverton
Supreme Loser
January 1999
avatar

There are various ways to solve it. In this particular case, I actually just rolled logging into the main application class. I build my CMS (actually it's a framework with a CMS module) on the fly. So while it may be useful to loop through the modules twice, by first calling a pre-processing function, I've just not had the need to yet.

Vanneto
Member #8,643
May 2007

That preprocessor function idea is great! There could be a handle_preprocessing() function! No double looping! ;D This is just great!

Well, this is basically everything I need for now. Just one tiny question. Its about style, its not particularly relevant, but I would like to know anyway.

What is better, that every class is extended from System and that System has all the functions that the module could use:

class Foo // Is it possible to extend from 2 different classes?
{
    public function Bar()
    {
        // If that is not possible, this could be a solution:
        global $system;

        $result = $system->db->query("SELECT something FROM everything");
        /* ... Moar code ... */
    }
}

This seems like its quaint. But yeah, I am infected from the "perfect design" virus. If its not perfect, its not good! Damn, I need to get over this! :P

In capitalist America bank robs you.

Matthew Leverton
Supreme Loser
January 1999
avatar

You can only extend one class.

I do this:

class Module_Foo extends Module
{
  public function bar()
  {
    $app = Application::getInstance();

    // in reality, I rarely make direct calls like this. 
    // usually an object (e.g., DBO_New) is used.
    $st = $app->db->prepare('...');
    $results = $st->getAll();
  }
}

I wanted to avoid using globals, although there's really nothing wrong with it considering PHP's syntax can otherwise get cumbersome (thanks to its retarded scope rules). Or you could create a $this->app variable in the Module constructor.

Vanneto
Member #8,643
May 2007

Sorry for being stupid, but how is DBO_New used?

I'm suspecting:

/* ... class ... */
    $db = DBO_New();
/* ... */

?

In capitalist America bank robs you.

Matthew Leverton
Supreme Loser
January 1999
avatar

It's supposed to be "DBO_News." DBO stands for "database object," but it's sort of a misnomer in that it doesn't necessarily have to be stored in the database. There's nothing exposed in the interface that requires it.

Basically a DBO is any conceptual object. It may or may not correspond one to one with a database table.

$news = DBO_News::find($foo); // the constructor is private
if ($news)
{
  $news->title = "A new title";
  // or $news['title'] = "A new title";
  $news->update();
}

It gives me runtime flexibility to only query the data I need. For instance:

$recent_news = DBO_News::findRecent();
foreach ($recent_news as $news)
{
  echo $news->reporter->name . "<br />";
}

The "reporter" isn't actually loaded by the initial query. It's used on demand.

Yes, this generates more SQL than is needed, but I don't care. The reason is that I cache HTML and data objects in memory if needed for performance.

In the above example, the findRecent() will generate a query that returns an array of news items without any linked information. Then for every unique reporter_id, it will load on demand the data for that person. That reporter data is then stored in memory so that the next time the page loads, only one query is run (findRecent()).

This is accomplished by:

1class DBO_News extends DBO
2{
3 public function __get($key)
4 {
5 switch ($key)
6 {
7 case 'reporter':
8 if (!$this->_reporter)
9 $this->_reporter = DBO_User::find($this->data['reporter_id']);
10 
11 return $this->_reporter;
12 break;
13
14 default: return parent::__get($key);
15 }
16 }
17}

The generic find() automatically will pull from memory if it can, thus eliminating simple one-off queries.

Again, this may seem like overkill if you know exactly how you will be using your data. But I chose this design with my templates in mind. I don't know when they will want to use extended information, nor do I want to force the template designers to use some SQL like syntax to inform PHP what to grab.

In this template, no extended data is used or queried:

<ul>
  <li mef:loop="n; news">
    {n.title}
  </li>
</ul>

But it's possible it might look like:

<ul>
  <li mef:loop="n; news">
    {n.title}, reported by {n.reporter.name}
  </li>
</ul>

Or even...

<ul>
  <li mef:loop="n; news">
    {n.title}, reported by {n.reporter.name} who has a game called {n.reporter.projects[0].title}
  </li>
</ul>

Vanneto
Member #8,643
May 2007

OK, but does the Module implement DBO News or is it a system object? I am thinking of letting each module have its own folder and then can have all classes it wants in there. That way the module-ness is not lost.

Oh, by the way, is it even useful to have these:

// include.php
if(!defined("IN_SITE")) die;

// index.php
define("IN_SITE", true);
include "include.php";

I'm getting bad headaches because of these plus __autoload! :S

In capitalist America bank robs you.

Matthew Leverton
Supreme Loser
January 1999
avatar

The DBO objects live outside of the modules. They can be used with or without the rest of the framework. Module_News "knows" that DBO_News exists, but the reverse isn't true. The other modules probably never need to use DBO_News, but they could if need be.

I don't know what you mean by your include question. (I don't even use includes.)

Vanneto
Member #8,643
May 2007

Its used so someone doesn't access your classes by doing:

http://www.mysite.com/include/myclass.class.php

And the defines are there to kill the script if its accessed directly. But I know what your gonna say, you have all the include files out of the web directory! ;D

In capitalist America bank robs you.

CGamesPlay
Member #2,559
July 2002
avatar

Quote:

Oh, by the way, is it even useful to have these:

No. Your include file doesn't execute any code, it just defines functions and classes. If a user accessed it, it would be a blank page.

--
Tomasu: Every time you read this: hugging!

Ryan Patterson - <http://cgamesplay.com/>

Matthew Leverton
Supreme Loser
January 1999
avatar

Quote:

And the defines are there to kill the script if its accessed directly. But I know what your gonna say, you have all the include files out of the web directory! ;D

Yes, I do.

class/                      All of my classes, including modules, DBOs, etc
template/                   System-wide (default / generic) templates 
site/dispatcher.php         The driver for everything
site/domain.com/template    Website templates
site/domain.com/web         Static Web Files (this is the doc root)

That's a subset of the directories I use. Each website has a mod_rewrite that directs everything to /.dispatcher.php which is just an Alias to the global dispatcher.php file.

Anything in the "web" directory can be accessed directly by the web browser, but in general it's just static images and downloads. Occasionally I may slip a PHP file in there, but only has a hack because I'm not interested (at the time) in creating a proper module for the functionality.

Thomas Fjellstrom
Member #476
June 2000
avatar

I'm interested in how thats actually setup, do you place it all in /var/www and have just one virtual host setup in apache? or?

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Matthew Leverton
Supreme Loser
January 1999
avatar

It's in /var/www/cms (I have other regular sites in /var/www/domain.com) with each cms-driven domain having its own vhost pointing to /var/www/cms/site/domain.com/web.

Thomas Fjellstrom
Member #476
June 2000
avatar

Ah, I figured as much, and the "links" are actual symlinks to the main controller?

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

 1   2   3   4 


Go to: