Asked  7 Months ago    Answers:  5   Viewed   74 times

I am trying to use namespaces in fatfree framework, but somehow its not able to find the class following is my setup

routes.ini

[routes]
GET /=SrcControllersIndex->index

index.php

namespace SrcControllers;

class Index {
    function index($f3) {
        $f3->set('name','world');
        echo View::instance()->render('template.htm');
    }
}

Global index.php

// Retrieve instance of the framework
$f3=require('lib/base.php');

// Initialize CMS
$f3->config('config/config.ini');

// Define routes
$f3->config('config/routes.ini');

// Execute application
$f3->run();

UPDATE:

Error:

Not Found

HTTP 404 (GET /)

• index.php:13 Base->run()

UPDATE 2:

config.ini

[globals]
; Where the framework autoloader will look for app files
AUTOLOAD=src/controllers/
; Remove next line (if you ever plan to put this app in production)
DEBUG=3
; Where errors are logged
LOGS=tmp/
; Our custom error handler, so we also get a pretty page for our users
;ONERROR=""
; Where the framework will look for templates and related HTML-support files
UI=views/
; Where uploads will be saved
UPLOADS=assets/

I'm not sure what's going wrong.

Thanks In advance.

 Answers

84

The autoloader of the Fat-Free Framework is very basic. It expects you to define one or more autoloading folders, each of them will map to the root namespace.

So let's say you define $f3->set('AUTOLOAD','app/;inc/') and your file structure is:

- app
- inc
- lib
  |- base.php
- index.php

Then a class named MyClass belonging to the FooBar namespace (full path: FooBarMyClass) should be stored in either app/foo/bar/myclass.php or inc/foo/bar/myclass.php (remember: we specified two autoloading folders).

NB: don't forget to specify namespace FooBar at the beginning of myclass.php (the autoloader won't do it for you).

--

So to answer your specific problem, having the following file structure:

- lib
  |- base.php
- src
  |- controllers
     |- index.php
- index.php

three configurations are possible:

Config 1

$f3->set('AUTOLOAD','src/controllers/')

Then all the files under src/controllers/ will be autoloaded, but remember : src/controllers/ maps to the root namespace, so that means the Index class should belong to the root namespace (full path: Index).

Config 2

$f3->set('AUTOLOAD','src/')

Then all the files under src/ will be autoloaded, which means the Index class should belong to the Controllers namespace (full path: ControllersIndex).

Config 3

$f3->set('AUTOLOAD','./')

Then all the files under ./ will be autoloaded, which means the Index class should belong to the SrcControllers namespace (full path: SrcControllersIndex).

Wednesday, March 31, 2021
 
DMTintner
answered 7 Months ago
68

The F3 instance variable which is declared at the very start of your index.php ($f3=require...) can be retrieved anywhere in the code using the static call $f3=Base::instance().

Anyway, for convenience purpose, at routing time this F3 instance as well as the route parameters are passed to the route handler. Therefore, instead of defining your route handler as:

function display() {
    $f3=Base::instance();
    echo 'I cannot object to an object' . $f3->get('PARAMS.page');
};

you could define it as:

function display($f3) {
    echo 'I cannot object to an object' . $f3->get('PARAMS.page');
};

or even better:

function display($f3,$params) {
    echo 'I cannot object to an object' . $params['page'];
};

These 3 functions are absolutely identical so you should pick up the one that you understand best. But you should remember that $f3 and $params are only passed at routing time, which means to 3 functions: the route handler, the beforeRoute() hook and the afterRoute() hook. Anywhere else in the code (including inside a class constructor), you should call Base::instance() to retrieve the F3 instance.

PS: your question being "why do i have to pass the $f3 class to the function?", I would suggest you to rename its title to reflect it.

UPDATE: Since release 3.2.1, the F3 instance is also passed to the constructor of the route handler class:

class myClass {
    function display($f3,$params) {
        echo 'I cannot object to an object' . $params['page'];
    }
    function __construct($f3) {
        //do something with $f3
    }
}
Wednesday, March 31, 2021
 
Sidarta
answered 7 Months ago
66

DB querying

There are 3 variants to loop through db results:

Without mapper:

Execute a SQL query and fetch the result set as an array of associative arrays:

$users = $db->exec('SELECT * FROM users');
foreach($users as $user)
  echo $user['name'];//associative array

With mapper->load:

Fetch mapper rows one by one (your method):

$user=new DBSQLMapper($db,'users');
$user->load('');
while(!$user->dry()) {
  echo $user->name;//db mapper
  $user->next();
}

With mapper->find:

Fetch the result set as an array of mappers:

$mapper=new DBSQLMapper($db,'users');
$users=$mapper->find('');
foreach($users as $user)
  echo $user->name;//db mapper

DB error handling

DBSQL is a subclass of PDO so it can throw catchable PDO exceptions. Since these are disabled by default, you need to enable them first. This can be done in 2 different ways:

  • at instantiation time, for all transactions:

    $db = new DBSQL($dsn, $user, $pwd, array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION ));

  • later on in the code, on a per-transaction basis:

    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

Once PDO exceptions are enabled, just catch them as other exceptions:

try {
  $db->exec('INSERT INTO mytable(id) VALUES(?)','duplicate_id');
} catch(PDOException $e) {
  $err=$e->errorInfo;
  //$err[0] contains the error code (23000)
  //$err[2] contains the driver specific error message (PRIMARY KEY must be unique)
}

This also works with DB mappers, since they rely on the same DBSQL class:

$db=new DBSQL($dsn,$user,$pwd,array(PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION));
$mytable=new DBSQLMapper($db,'mytable');
try {
  $mytable->id='duplicate_id';
  $mytable->save();//this will throw an exception
} catch(PDOException $e) {
  $err=$e->errorInfo;
  echo $err[2];//PRIMARY KEY must be unique
}
Wednesday, March 31, 2021
 
redrom
answered 7 Months ago
45

You don't have to define a route for this. F3 will automatically generate a 404 status code for any non-defined route.

If you want to define a custom error page, you need to set the ONERROR variable.

Here's a quick example:

$f3->route('GET /','App->home');
$f3->set('ONERROR',function($f3){
  echo Template::instance()->render('error.html');
});
$f3->run();

with error.html defined as:

<!DOCTYPE html>
<head>
<title>{{@ERROR.text}}</title>
</head>
<body>
  <h1>{{@ERROR.text}}</h1>
  <p>Error code: {{@ERROR.code}}</p>
</body>
</html>

Now if you call any non-defined route like /foo, the template error.html will be rendered with a 404 status code.

NB: this works for other error codes. Other error codes are triggered by F3 or your application with the command $f3->error($status), $status being any valid HTTP status code (404, 500, 403, etc...)

Wednesday, March 31, 2021
 
Manju
answered 7 Months ago
94

You need to fully qualify the relative class name Yii.

The most convenient way to do this is by importing the class: just add use Yii; below your namespace declaration.

Saturday, May 29, 2021
 
Jimenemex
answered 5 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :