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!
Related
I'm currently rebuilding my vanilla-PHP-App with Laravel and I have the following problem.
I have multiple database-tables, that represent word categories (noun, verb, adverb, ...). For each table I created a separate Model, a route::resource and a separate resource-Controller. For example:
NomenController.php
public function show($id)
{
$vocab = Nomen::find($id);
return view('glossarium.vocab_update', compact('vocab'));
}
and
VerbController.php
public function show($id)
{
$vocab = Verb::find($id);
return view('glossarium.vocab_update', compact('vocab'));
}
...which are essentially the same except the Model class.
I don't want to create a separate Controller for each model, that does exactly the same. What would be the most simple and elegant way to solve this?
Should I just create a VocabController.php and add a parameter for the Model-name like:
Route::resource('/vocab/{category}', 'VocabController');
and then add a constructor method in this controller like
public function __construct ($category) {
if ($category == 'nomen') {
$this->vocab = App\Nomen;
}
else if ($category == 'verb') {
$this->vocab = App\Verb;
}
}
I wonder if there is a simpler method to do that. Can I somehow do this with Route Model Binding?
Thanks in advance
Simply create a trait like this in App\Traits, (you can name it anything... Don't go with mine though... I feel its pretty lame... :P)
namespace App\Traits;
trait CommonControllerFunctions {
public function show($id) {
$modelObject = $this->model;
$model = $modelObject::find($id);
return view('glossarium.vocab_update', compact('model'));
}
}
and in your NomenController and VerbController, do this:
use App\Traits\CommonControllerFunctions;
class NomenController {
use CommonControllerFunctions;
protected $model = Nomen::class;
}
and
use App\Traits\CommonControllerFunctions;
class VerbController {
use CommonControllerFunctions;
protected $model = Verb::class;
}
Note: Please note that this example is just a work-around for your particular situation only... Everyone practices code differently, so this method might not be approved by all...
I think the simpliest way it to create only one controller, eg VocabController with methods nomen, verb and whatever you want.
Routes:
Route::get('/vocab/nomen/{nomen}', 'VocabController#item');
Route::get('/vocab/verb/{verb}', 'VocabController#item');
And the model binding:
Route::model('nomen', 'App\Nomen');
Route::model('verb', 'App\Varb');
Then your method shoud look like that:
public function item($item)
{
return view('glossarium.vocab_update', $item);
}
Keep in mind, that $item is already fetched model from the database.
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.
So, lets say I have a global view and specific view.
In global view, the url may look like this (http://example.com/index.php/controller/method/)
Where when it come to the specific page view, the url will turn like this:
(http://example.com/index.php/controller/method/1989-03-25)
The difference between the global view and the specific page view is, if in the global view it shows the information in general, but in the specific page view it shows based on the detail or the date.
Of course, not only have different view, but also they will have different function of models.
The point is, I just want to make the url keep in order (which it mean there is no change in the name of the controller method).
How to do this. Thanks in advance.
You create just one param into your function. And set the param value is null. like this
class YOUR_CLASS extends CI_controller {
public function method($params=null) //null is Empty value
{
if($params)
{
//load your modal and view with your param
}
else
{
//load your modal and view
}
}
}
This method supports the following type of URL's without any issue.
http://example.com/index.php/YOUR_CLASS/method/
http://example.com/index.php/YOUR_CLASS/method/1989-03-25
Hope this will help you....
This class used to wrap CI_controller, my_base_controller will override CI_controller methods for depends to your project needs
class my_base_controller extends CI_controller {
}
You can load models by known states and define it in my_base_controller class.
class my_controller extends my_base_controller{
public function method($params=null) //null is option value
{
}
}
Good luck!
You can add additional parameter in your method like:
class Your_controller extends CI_controller {
public function method($params = null)
{
// Your Code Here with the condition for processing of the $params variable
}
}
in which that $params can be something in your URL like:
http://example.com/controller/method/your-dynamic-params
So if the $params is null you will call the model the views the general and if the $params has a specific value you can call other model by using if or switch conditional statements. Hope this helps...
Update with Example
you can use the $params variable like this:
if ($params == "1991") {
$this->load->view('general.html', $params);
} elseif ($params == "1992") {
$this->load->view('year_1992.html', $params);
} else {
$this->load->view('other_years.html', $params)
}
in this way you can use the $params as a conditional variable to load different views.
or using switch:
switch($params) {
case '1991':
$this->load->view('general.html', $params);
break;
case '1992':
$this->load->view('year_1992.html', $params);
break;
default:
$this->load->view('other_years.html', $params)
}
Note: Use a helper method so you can avoid fat controllers because it will be hard to test your methods if you have a several lines of codes in a function.
I'm pretty new to Laravel and am struggeling with the following problem at the moment:
I want to create a User-Group-Management. So I want to add Users to a Group and add Rights to these Groups, to have an authorization. My aim is to have a User::hasRight('exampleRight') function, which i can easily call. For that I wanted to have a function chaining inside of the User-Class. I have this function to create a Connection to the UserGroup-Table (which connects an User-ID to a Group-ID):
public function role()
{
return $this->hasOne('UserGroup');
}
The next function shall return an Array of rights. My plan was to write something like
public function rights()
{
$rights = Groups::find($this->role()->groups_id)->rights;
return $rights;
}
The GroupsModel of course has this function to get the Rights:
public function rights()
{
return $this->hasMany('Rights');
}
But obviously $this->role()->groups_id doesn't give me the groups_id but instead throws the Error Undefined property: Illuminate\Database\Eloquent\Relations\HasOne::$groups_id. When i leave the ->groups_id out, and do add it instead in the controller like:
(Attention Controller, no model!)
public function getUserRole()
{
return User::find(10)->rights->groups_id;
}
it gives me the right answer. Can somebody tell me, whats the error? I don't really get whats wrong...
Thanks you in advance!
Regards
In this particular method you need to remove the parenthesis:
public function rights()
{
$rights = Groups::find($this->role->groups_id)->rights;
return $rights;
}
When you do role() you're actually telling Laravel to return the relation object, so you can do things like
$this->role()->where('x', '=', 'y')->get();
Why do you have an intermediary table between User and Group? Simply give the User a group_id. Here is how I have built this in the past.
User belongsTo Group
Group belongsToMany Right
// Inside User Model
public function can($name)
{
foreach ($this->group->rights as $right)
{
if ($right->name == $name)
{
return true;
}
}
return false;
}
// Elsewhere
if (!$user->can('edit_blogs'))
{
return 'FORBIDDEN';
}
Look into eager loading to mitigate performance issues.
Look into filters to apply these access restrictions at the controller or route level.
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');