I am recently started using laravel, i am using following restful approach, please suggest if this approach is good...
Author controller
class AuthorsController extends BaseController {
public $restful = true;
public function getIndex()
{
return View::make('authors.index')->with('title', 'Showing Authors')->with('authors', Author::all());
}
public function newAuthor()
{
if(Request::isMethod('post')){
$author = Author::create(array(
'name' => Input::get('name'),
'bio' => Input::get('bio')
));
if($author->id) return Redirect::route('authors')->with('message', 'The Author was created successfully!');
}
else{
return View::make('authors.new')->with('title', 'New Author');
}
}
}
Routes
Route::get('authors', array('as' => 'authors', 'uses' => 'AuthorsController#getIndex'));
Route::match(array('GET', 'POST'), 'authors/new', array('as' => 'new_author', 'uses' => 'AuthorsController#newAuthor'));
Please suggest if i am using correct approach for create method, and i am using same method for add form and post request.
thanks.
Your code probalby works, but you could improve it by joining your routes, controllers and migrations in one resource.
With laravel generator package
After installing that package, with the following command:
Route::resource("path","SomeController");
You will get generated resources for you app. That includes list of restful controller actions.
For example you for main GET method you will have generated index action in you controller.
Close. You shouldnt need to test the request method in a controller action if the routes/controller is set up properly.
Author Controller
class AuthorsController extends BaseController {
public function getIndex()
{
return View::make('authors.index')->with('title', 'Showing Authors')->with('authors', Author::all());
}
public function getNew()
{
return View::make('authors.new')->with('title', 'New Author'); //this view should contain a form that POST's to /authors/new
}
public function postNew() {
$author = Author::create(Input::all()); //note: use $fillable on your model here to prevent any extra fields from breaking things
return Redirect::action('AuthorsController#getNew')->with('message', 'The Author was created successfully!');
}
}
Routes
Route::controller("/authors", "AuthorsController")
Related
I've been trying to send data from controller to view in CodeIgniter 4, let me just...
Model:
class KomikModel extends Model
{
protected $table = 'komik';
protected $useTimestamps = true;
protected $allowedFields = ['judul', 'slug', 'penulis', 'penerbit', 'sampul'];
public function getKomik($slug = false)
{
if ($slug == false) {
return $this->findAll();
}
return $this->where(['slug' => $slug])->first();
}
}
Controller:
public function edit($slug)
{
$data = [
'title' => 'Form Ubah Data',
'validation' => \Config\Services::validation(),
'komik' => $this->komikModel->getKomik($slug)
];
return view('komik/edit', $data);
}
View:
<?= dd($komik); ?>
After all that, $komik that arrived in the view comes out as null. Why, what did I do wrong? Thanks in advance.
PS: Somehow, it works fine with other methods. currently there are 3 other methods using the model to send data from controller towards the view. but only at this edit method the problem occurs.
The issue is with this particular line.
return $this->where(['slug' => $slug])->first();
Model Class doesn't have any "where" method defined in it. It borrows it from Builder Class. This is called object composition. You can certainly try to have your own method with a similar implementation as in the Model class but I would recommend against it since it is an antipattern.
A better approach would be to have a single class that is responsible for all the querying or filtering as in Repository Pattern. You can do a bit of research on that.
I'm trying to create a trait (for models) that would automatically write all changes made to the model in 'adjustments' table. It would save changes in before and after jsons.
This is the code so far (from Laracasts):
trait LoggingTrait
{
public static function boot()
{
parent::boot();
static::updating(function($action){
$action->adjustments()->create(
[
'user_id' => Auth::id(),
'before' => json_encode(array_intersect_key($action->getOriginal(), $action->getDirty())),
'after' => json_encode($action->getDirty())
]);
});
}
public function adjustments()
{
return $this->morphMany(Adjustment::class, 'adjustable');
}
}
This is working very well, except it doesn't save changes to related models.
To make it more clear, this is my Action model:
class Action extends Model
{
use LoggingTrait;
public function actionTypes()
{
return $this->belongsToMany(ActionType::class);
}
public function users()
{
return $this->belongsToMany(User::class);
}
public function spreadingMaterial()
{
return $this->belongsTo(SpreadingMaterial::class);
}
}
The trait logs all the changes made to the actual Action model, but doesn't care for the changes made to the $action->users(), $action->spreadingMaterials() and $action->actionTypes(). How would I get these changes within the static::updating(...) event?
Or if that is not possible, any other idea on how to tackle this problem is more than welcome.
I am using cakephp-2.x. I have one function name user_info() in the UsersController.php i want to access this in another controller name MessagesController.php
Code -
UsersController.php
public function user_info(){
$user_id=$this->Session->read('Auth.User.id');
$data=$this->User->findById($user_id);
$this->set('user_info',$data);
}
MessagesController.php
public function index(){
//$userInfo=new UsersController();
//$userInfo->user_info();
$this->user_info();
pr($data);
}
Error Message-
Fatal Error
Error: Call to undefined method MessagesController::user_info()
File: E:\xampp\htdocs\2014\myshowcam\msc\app\Controller\MessagesController.php
Line: 18
Notice: If you want to customize this error message, create app\View\Errors\fatal_error.ctp
Typically if you're trying to access a function in one controller from another controller you have a fundamental flaw in your project's logic.
But in general object usage is thus:
$otherController = new whateverMyControllerNameIs();
$otherController->functionName();
However I'm not familiar enough with cake to tell you the pitfalls of doing such a thing. For example I have no idea what this would do to routes or what other variables/objects are required to initialize a controller correctly.
EDIT:
Ref: CakePHP 2.3.8: Calling Another Controller function in CronController.php
App::import('Controller', 'Products'); // mention at top
// Instantiation // mention within cron function
$Products = new ProductsController;
// Call a method from
$Products->ControllerFunction();
Try requestAction function of cakephp
$result = $this->requestAction(array('controller' => 'users', 'action' => 'user_info'));
Why would a simple, When can complicated?
All the information for a registered user of User model is accessible in the following manner:
AppController.php
public $user_info; /* global scope */
public function beforeFilter(){
$this->user_info = $this->Auth->user(); // for access user data in any controller
$this->set('user_info_view',$this->Auth->user()); // for access user data in any view or layout
}
MessagesController.php
public function index(){
debug($this->user_info);
$my_messages = $this->Message->find('all',
array('conditions' => array('Message.user_id' => $this->user_info['id']))
}
....
layout or view.ctp
<?php echo $user_info_view['name']; ?> // email, etc
Why not take advantage of the way CakePHP handles relationships? There's a very easy way to achieve what you're trying to do without extending controllers or loading in additional controllers which seems excessive for your example.
Inside AppController's beforeFilter()
Configure::write('UserId', $this->Session->read('Auth.User.id'));
This will allow you to access the UserID from your models
Inside your User's model, create the following function
/**
* Sample query which can be expanded upon, adding fields or contains.
*
* #return array The user data if found
*/
public function findByUserId() {
$user = $this->find('first', array(
'conditions' => array(
'User.id' => Configure::read('UserId')
)
));
return $user;
}
Inside your Users controller (Minimal is better, no?)
public function user_info() {
$this->set('user', $this->User->findByUserId());
}
Inside your Messages controller
public function index() {
$this->set('user', $this->Message->User->findByUserId());
// --- Some more stuff here ---
}
And that's it, no need to be extending controllers, just make sure your Message and User model are related to each other, failing that you can bindModel or use ClassRegistry::init('User')-> for example.
New to laravel and trying to work out the best way to structure my app.
It has both an admin interface and an API (JSON, angularjs front-end).
my routes currently look like:
Route::group(array('prefix' => 'admin', 'before' => 'auth.admin'), function()
{
Route::any('/', array('as' => 'admin.index', function() {
return View::make('admin.index');
}));
Route::resource('countries.products', 'ProductsController');
Route::resource('countries', 'CountriesController');
Route::resource('orders', 'OrdersController');
});
// Route group for API versioning
Route::group(array('prefix' => 'api/v1'), function()
{
Route::resource('products', 'APIProductsController', array('only' => array('index', 'show')));
Route::resource('orders', 'APIOrdersController', array('only' => array('store', 'update')));
});
There is a lot of duplicated logic in eg, the OrdersController & APIOrdersController. Should I re-use a single controller somehow, maybe with content-negotation? or is it better to modify OrdersController to query the API routes instead of using eloquent?
or is there another, better way?
As I see it, I would extract all object creation logic to a proper class (sounds like a good case for a repository). This class should only know about the parameters it has to receive, and respond accordingly. For example:
class EloquentOrder implements OrderRepositoryInterface {
// Instance of OrderValidator,
// assuming we have one
protected $validator;
public function create($params)
{
// Pseudo-code
$this->validator = new Ordervalidator($params);
if ($this->validator->passes())
create and return new Order
else
return validator errors
}
}
Then, each of your modules can use this functionality inside its controllers.
In your API, you could have this:
class APIOrderController extends APIController {
protected $repository;
public function __construct(OrderRepositoryInterface $repository)
{
$this->repository = $repository;
}
public function create()
{
// Let's imagine you have an APIAuth class which
// authenticates via auth tokens:
if (APIAuth::check()) {
$params = Input::all();
return $this->repository->new($params);
}
return Response::json(['error' => 'You are not authorized to create orders'], 401);
}
}
While in your administration module, you could have:
class AdminOrderController extends AdminController {
protected $repository;
public function __construct(OrderRepositoryInterface $repository)
{
$this->repository = $repository;
}
public function create()
{
// Now, let's imagine Auth uses a different authentication
// method, and can check for specific permissions
if (Auth::check() && Auth::hasPermission('create.orders')) {
$params = Input::all();
return $this->repository->new($params);
}
return Redirect::home()->with('message', 'You are not authorized to create orders');
}
}
As you can see, this allows you to reuse your object creation logic in different contexts. In the example I've used different authentication methods and responses just to show flexibility, but this will really depend on your project requirements.
Having the following controller:
class Admin_Images_Controller extends Admin_Controller
{
public $restful = true;
public function __construct()
{
parent::__construct();
}
public function get_index($id)
{
echo $id;
}
I don't understand why when I access it with no parameter for ID it works, as I get an error says missing parameter for ... but when I actually try to pass a parameters at http://site/admin/images/12 I get a 404 error. What am I missing?
I tried setting the following in my routes, no success either:
Route::any('admin/images', array(
'as' => 'admin_images',
'uses' => 'admin.images#index',
));
//or
Route::any('admin/images/(:any)', array(
'as' => 'admin_images',
'uses' => 'admin.images#index',
));
It seams that my issues with wildcards, 90% happen in my test linux envirnonment (ubuntu). Here's my routes.php that I'm currently using http://pastebin.com/f86A3Usx
it could be that you're using the same alias (admin_images) and also, check your order - put the more specific ones first, and more generic as you go down, like so:
Route::any('admin/images/(:any?)', array('uses' => 'admin.images#index'));
Have removed the alias, just for readability.
Route::get('admin/images/(:any)', 'admin.images#index');
You should make the $id parameter optional, by passing a default value (like null/false/1)
public function get_index($id = null)
{
if($id){
echo $id;
}else{
echo "No ID given!";
}
}
And use (:any?) in your route.
Updated Routes:
Route::any('admin/images/(:any?)', array(
'as' => 'admin_images',
'uses' => 'admin.images#index',
));
You can simplify your routing by combining your routes for each endpoint. By adding the "?" into your first parameter, this mean that anything can be present, but doesn't have to be. So both /admin/images and /admin/images/1234 are covered.
Updated Controller:
class Admin_Images_Controller extends Admin_Controller
{
public $restful = true;
public function __construct()
{
parent::__construct();
}
public function get_index($id=null)
{
echo $id;
}
// ...
}
With the addition of "= null" into your method parameter, you can now handle both routes into this function. A simple check of "equals null" within your method should put you well on your way to covering each senario.