I'm new to Codeigniter, and I'm trying to get accustomed to it by converting an old site into CI.
One thing I'm having trouble understand is the routing. If I don't want to have my url structure like /controller/method/id, I have to change it to something like $route['controller/(:num)'] = "controller/method/$1"; in routes.php. It just seems inefficient to me, is there something else I should be doing?
For example, on my site, the urls are /game/4242 and /player/SomeDude
Well, routing is effecient - the alternative is remapping your controllers.
Let's take a look at both possibilities.
An imaginary situtation:
At a later point, you'd like to allow your users to show badges/medals/achievements/something on their profile.
With routing, you can achieve it like this:
$route['player/(:any)/(:any)'] = "player/show_$2/$1";
$route['player/(:any)'] = "player/show_profile/$1";
And your controller could in turn look like this:
class Player extends CI_Controller
{
public function show_profile( $username )
{
// the profile info
}
public function show_badges( $username )
{
// the profiles badges
}
public function show_scores( $username )
{
// the profiles scores
}
}
}
Basically, this allows you to simply add another method in your controller prefixing the method with show_ (like public method show_friends( $username ) )and you can access it instantly by going to /player/SomeDude/friends
Looking at the alternative, remapping your controller would allow you not to use routes, but write a controller like this:
class Player extends CI_Controller
{
public function _remap($username, $params = array())
{
if(empty($username))
show_404();
$this->user = $this->user_model->find($username);
if(count($params) == 0)
$method = 'index';
else
$method = $params[0];
unset($params[0]); //No need to send the method along as a parameter
$method = 'process_'.$method;
if (method_exists($this, $method))
{
return call_user_func_array(array($this, $method), $params);
}
show_404();
}
public method process_index()
{
// the profile info
}
public method process_badges()
{
// the profiles badges
}
public method process_scores()
{
// the profiles scores
}
}
Personally, I like routing. I think it's transparent and makes my controllers look cleaner.
Related
Trying to load different views and methods depending on which view the user is browsing.
Views:
public function edit()
{
if("SOMETHING")return View::make('store_edit');
If("SOMETHING")return View::make('product_edit');
}
Methods:
public function destroy($id)
{
if(SOMETHING){
$store = Store::find($id);
$store->delete();
return redirect('/store');
}
if(SOMETHING){
$product = Product::find($id);
$product->delete();
return redirect('/product');
}
}
What can be used in the if() statements depending on which view is browsed in order to delete the right item and not having to rewrite the functions for each table.
There isn't a simple way to get information about which view was displayed in a previous request, and that's probably not what you want. You should create separate controllers/routes for both "products" and "store". Then you can do away with that view logic altogether.
To somewhat answer your question, you can access information about the current route with the Route facade.
$route = Route::current();
$name = Route::currentRouteName();
$action = Route::currentRouteAction();
Read Laravel routing:
https://laravel.com/docs/5.6/routing#route-parameters
Route
Route::get('something/{param}', 'SomeController#edit');
Controller
...
public function edit($param) {
if ($param === $expectedParam) {...} else {...}
}
Also you can make $param optional:
Route
Route::get('something/{param?}', 'SomeController#edit');
Controller (don't forget to give a default value)
...
public function edit($param = null) {
if ($param === $expectedParam) {...} else {...}
}
Please do not use same controller for diferent models.
if has diferent view and diferent functions this HAVE to has diferent controllers
Again, Laravel is a beautiful framework, dont do this!
I am using code igniter and my URL structure looks like this:
http://localhost:8888/project/register
Register in this case is the controller.
I am trying to pass an ID to this URL, something like so:
http://localhost:8888/project/register/1234 so that I can use this value in my controller.
I am running into an issue trying to figure that out since the position of this ID is meant for the name of a method within the controller.
Is it possible to add a value in this position without it thinking its a method or do I need to do something like :
http://localhost:8888/project/register?code=1234 ?
Trying to access it like so: $registerPin = $this->common->nohtml($this->uri->segment(2));
Update:
As a hack workaround, I did this.. Any better ways?
URL: http://localhost:8888/project/register/c/1234
/**
* Pass our signup code for demo purposes
*/
public function c(){
// Did we come here from a Sign-up Pin?
$registerPin = $this->common->nohtml($this->uri->segment(3));
if($registerPin){
$this->session->set_userdata(array(
'registerPin' => $registerPin
));
}
// Redirect to register
redirect(site_url("register"));
}
public function index()
{
echo $this->session->userdata('registerPin');
}
One solution would be to use the Controller's _remap functionality, although I've not used this the way you're intending to use it. (Hopefully it works)
See: https://www.codeigniter.com/userguide3/general/controllers.html#remapping-method-calls
<?php
class Register extends MY_Controller{
public function __construct()
{
parent::__construct();
}
// -----------------------------------------------------------------------
/**
* Controller remapping
*/
public function _remap( $method, $params = [] )
{
if( is_numeric( $method ) )
{
$this->index( $method );
}
else
{
$this->$method();
}
}
// -----------------------------------------------------------------------
/**
* Index method
*/
public function index( $num )
{
# code...
}
// -----------------------------------------------------------------------
}
Another solution would be to use URI routing.
See: https://www.codeigniter.com/userguide3/general/routing.html
$route['register/(:num)'] = 'register/index/$1';
Neither of these examples may be fully functional in your case, but should be close to what you need.
I am somewhat new to OOP, although I know about interfaces and abstract classes a bit. I have a lot of resource controllers that are somewhat similar in the bigger scheme of things, they all look like the example below, the only main difference is the index and what I pass to the index view.
What I simply need to know is, can I OO things up a bit with my resource controllers? For example, create one "main" resource controller in which I simply pass the correct instances using an interface for example? I tried playing around with this but I got an error that the interface wasn't instantiable, so I had to bind it. But that means I could only bind an interface to a specific controller.
Any advice, tips and pointers will help me out :)
class NotesController extends Controller
{
public function index()
{
$notes = Note::all();
return view('notes.index', compact('notes'));
}
public function create()
{
return view('notes.create');
}
public function show(Note $note)
{
return view('notes.show', compact('note'));
}
public function edit(Note $note)
{
return view('notes.edit', compact('note'));
}
public function store(Request $request, User $user)
{
$user->getNotes()->create($request->all());
flash()->success('The note has been stored in the database.', 'Note created.');
return Redirect::route('notes.index');
}
public function update(Note $note, Request $request)
{
$note->update($request->all());
flash()->success('The note has been successfully edited.', 'Note edited.');
return Redirect::route('notes.index');
}
public function delete($slug)
{
Note::where('slug', '=', $slug)->delete();
return Redirect::to('notes');
}
}
Note: Totally my opinion!
I would keep them how you have them. It makes them easier to read and understand later. Also will save you time when you need to update one to do something different from the rest. We tried this in a project I worked on and while granted it wasn't the best implementation, it is still a pain point to this day.
Up to you though. I'm sure people have done that in a way that they love and works great. Just hasn't been the case in my experience. I doubt anyone would look at your code though and criticize you for not doing it.
In Case you need to bind different Model instanses then you may use Contextual Binding, for example, put the following code in AppServiceProvider's register() method:
$this->app->when('App\Http\Controllers\MainController')
->needs('Illuminate\Database\Eloquent\Model')
->give(function () {
$path = $this->app->request->path();
$resource = trim($path, '/');
if($pos = strpos($path, '/')) {
$resource = substr($path, 0, $pos);
}
$modelName = studly_case(str_singular($resource));
return app('App\\'.$modelName); // return the appropriate model
});
In your controller, use a __construct method to inject the model like this:
// Put the following at top of the class: use Illuminate\Database\Eloquent\Model;
public function __construct(Model $model)
{
$this->model = $model;
}
Then you may use something like this:
public function index()
{
// Extract this code in a separate method
$array = explode('\\', get_class($this->model));
$view = strtolower(end($array));
// Load the result
$result = $this->model->all();
return view($view.'.index', compact('result'));
}
Hope you got the idea so implement the rest of the methods.
I am looking to make a custom route using the CodeIgniter framework. I am trying to make the URL like so:
http://localhost/accounts/Auth.dll?signin
So far I have tried adding the following to my routes.php config file:
$route['accounts/Auth.dll?signin'] = "accounts/signin";
but as you would guess, it doesn't work. I have also tried escaping the characters like this:
$route['accounts/Auth\.dll\?signin'] = "accounts/signin";
and that doesn't work either. I've also tried including the leading and trailing slashes .. that didn't work either. Anyone know by chance what could solve my issue?
I highly recommend to use a SEF routing.
But if for any reason you're not eager to, you could check the query string inside the Accounts Controller, and then invoke the proper method, as follows:
Router:
$route['accounts/Auth.dll'] = "accounts";
Controller:
class Accounts extends CI_Controller
{
public function __construct()
{
# Call the CI_Controller constructor
parent::__construct();
# Fetch the query string
if ($method = $this->input->server('QUERY_STRING', TRUE)) {
# Check whether the method exists
if (method_exists($this, $method)) {
# Invoke the method
call_user_func(array($this, $method));
}
}
}
protected function signin()
{
# Your logic here
}
}
This allows you to invoke the methods by query string automatically.
I am not sure, that its okay to use GET-params in routes.php config.
Try such way:
routes.php
$route['accounts/Auth.dll'] = "accounts/index";
accounts.php
public function index() {
if ($this->input->get('signin') != false) {
$this->signin();
}
}
private function signin() {
// some code
}
But, as for me, it's bad way.
I recommend you just use another routing:
/accounts/Auth.dll/signin
And etc.
I like MVC (a lot), and I am trying to teach myself a framework of MVC architecture in all the major web languages of today.
I am currently on CodeIgniter and PHP. I searched online for a way to make the same function behave different for a POST and GET but couldn't find anything. Does CodeIgniter have this feature?
If you've used Ruby On Rails or ASP.NET MVC you'll know what I'm talking about, in them frameworks we can do this:
[GET]
public ActionResult Edit(int Id)
{
// logic here for GET
}
[POST]
public ActionResult Edit(EntityX EX)
{
// logic here for POST
}
I am so used to this, that I am finding it hard wrapping my head around how to get the same smooth functionality without that useful ability.
Am I missing something? How can I achieve the same thing in CodeIgniter?
Thanks
Am I missing something? How can I achieve the same thing in
CodeIgniter?
if you want to learn how to truly approach MVC in PHP, you can learn it from Tom Butler articles
CodeIgniter implements Model-View-Presenter pattern, not MVC (even if it says so). If you want to implement a truly MVC-like application, you're on the wrong track.
In MVP:
View can be a class or a html template. View should never be aware of a Model.
View should never contain business logic
A Presenter is just a glue between a View and the Model. Its also responsible for generating output.
Note: A model should never be singular class. Its a number of classes. I'll call it as "Model" just for demonstration.
So it looks like as:
class Presenter
{
public function __construct(Model $model, View $view)
{
$this->model = $model;
$this->view = $view;
}
public function indexAction()
{
$data = $this->model->fetchSomeData();
$this->view->setSomeData($data);
echo $this->view->render();
}
}
In MVC:
Views are not HTML templates, but classes which are responsible for presentation logic
A View has direct access to a Model
A Controller should not generate a response, but change model variables (i.e assign vars from $_GET or $_POST
A controller should not be aware of a view
For example,
class View
{
public function __construct(Model $model)
{
$this->model = $model;
}
public function render()
{
ob_start();
$vars = $this->model->fetchSomeStuff();
extract($vars);
require('/template.phtml');
return ob_get_clean();
}
}
class Controller
{
public function __construct(Model $model)
{
$this->model = $model;
}
public function indexAction()
{
$this->model->setVars($_POST); // or something like that
}
}
$model = new Model();
$view = new View($model);
$controller = new Controller($model);
$controller->indexAction();
echo $view->render();
The parameters only allow you to retrieve GET variables. If you want to get the POST variables, you need to use the Input library which is automatically loaded by CodeIgniter:
$this->input->post('data');
So, in your case, it would be:
public function edit($id = -1)
{
if($id >= 0 && is_numeric($id))
{
// logic here for GET using $id
}
else if($id === -1 && $this->input->post('id') !== false)
{
// logic here for POST using $this->input->post('id')
}
}
Note that you can also use this library to obtain GET, COOKIE and SERVER variables:
$this->input->get('data');
$this->input->server('data');
$this->input->cookie('data');