I'm really new to Laravel, so I have this question:
If I have 2 tables in the database, say: positions and vectors and I want to be able
to edit/add/update/delete them, what kind of model/controller/view structure do I need?
Am I off with creating controllers for both positions and vectors?
Should I create a Settings controller and just create the models for positions and vectors?
This is all up to you really, but I would have a model for each, a repository for all the logic, probably called SettingsRepository.php and then just use that repository for wherever you need to use that code. You'd also have to modify your composer.json file and make sure the folder you are putting your repositories in is in the autoload section.
As for your controllers, you'd probably only need one which would grab the data from the repository and insert it into the view. When I think of settings, I think of settings you'd need in other areas of your app as well and with the repository, all that information would be easily accessible to other controllers.
Models handle the direct interactions with your database. Create a "Position" model and a "Vector" model. When you do, you will extend the Eloquent model, and you will quickly have a model that knows how to add, delete, update, save, etc.
http://laravel.com/docs/eloquent
Create one view for each page you want the user to see. (You'll create master template views eventually, but don't worry about this for now).
http://laravel.com/docs/responses#views
Controllers juggle the data between the views and the models. There is a section on this in the Laravel docs but I don't have enough rep points to post more than 2 links.
Group your controller functionality in a way that makes the most sense. If all of your website routes are going to start with "/settings", that a red flag that you might just need one "settings" controller.
Here is an extremely simplified example of what you might be wanting to do. There are a lot of different ways to accomplish what you are wanting. This is one example.
// View that you see when you go to www.yoursite.com/position/create
// [stored in the "views" folder, named create-position.php]
<html><body>
<form method="post" action="/position/create">
<input type="text" name="x_coord">
<input type="text" name="y_coord">
<input type="submit">
</form>
</body></html>
// Routing [in your "routes.php" file]
// register your controller as www.yoursite.com/position
// any public method will be avaliable at www.yoursite.com/methodname
Route::controller('position', 'PositionController');
// Your Position Controller
// [stored in the "controllers" folder, in "PositionController.php" file
// the "get" "post" etc at the beginning of a method name is the HTTP verb used to access that method.
class PositionController extends BaseController {
public function getCreate()
{
return View::make('create-position');
}
public function postCreate()
{
$newPosition = Position::create(array(
'x_coord' => Input::get('x_coord'),
'y_coord' => Input::get('y_coord')
));
return Redirect::to('the route that shows the newly created position');
}
}
// Your Position Model
class Position extends Eloquent {
// the base class "Eloquent" does a lot of the heavy lifting here.
// just tell it which columns you want to be able to mass-assign
protected $fillable = array('x_coord', 'y_coord');
// yes, nothing else required, it already knows how to handle data
}
Related
I am working on a newly created Phalcon project, and I don't really know how to actually use multiples views.
What is the entry point? I don't really know when each method in the controller is called, under which conditions, etc.
Where is the control flow defined? is it based in the name of the view? or is there a place where you can register them?
Phalcon is a bit different than other well-known PHP frameworks, in that not much is pre-configured or pre-built by default. It's quite loosely-coupled. So you have to decide where and how your control flow will work. This means that you will need to dig deeper in the documentation and also that there could be different way to achieve the same thing.
I'm going to walk you through a simple example and provide references, so you can understand it more.
1) You would start by defining a bootstrap file (or files) that will define the routes, or entry points, and will setup and create the application. This bootstrap file could be called by an index.php file that is the default file served by the web server. Here is an example of how such bootstrap file will define the routes or entry points (note: these are just fragments and do not represent all the things that a bootstrap file should do):
use Phalcon\Di\FactoryDefault;
// initializes the dependency injector of Phalcon framework
$injector = new FactoryDefault();
// defines the routes
$injector->setShared('router', function () {
return require_once('some/path/routes.php');
});
Then it the routes.php file:
use Phalcon\Mvc\Router;
use Phalcon\Mvc\Router\Group as RouterGroup;
// instantiates the router
$router = new Router(false);
// defines routes for the 'users' controller
$user_routes = new RouterGroup(['controller' => 'users']);
$user_routes->setPrefix('/users');
$user_routes->addGet('/show/{id:[0-9]{1,9}}', ['action' => 'show']);
$router->mount($user_routes);
return $router;
Im defining routes in an alternate way, by defining routes groups. I find it to be more easy to organize routes by resource or controller.
2) When you enter the url example.com/users/show/123, the routes above will match this to the controller users and action show. This is specified by the chunks of code ['controller' => 'users'], setPrefix('/users') and '/show/{id:[0-9]{1,9}}', ['action' => 'show']
3) So now you create the controller. You create a file in, let's say, controllers/UsersController.php. And then you create its action; note the name that you used in the route (show) and the suffix of Action:
public function showAction(int $id) {
// ... do all you need to do...
// fetch data
$user = UserModel::findFirst(blah blah);
// pass data to view
$this->view->setVar('user', $user);
// Phalcon automatically calls the view; from the manual:
/*
Phalcon automatically passes the execution to the view component as soon as a particular
controller has completed its cycle. The view component will look in the views folder for
a folder named as the same name of the last controller executed and then for a file named
as the last action executed.
*/
// but in case you would need to specify a different one
$this->view->render('users', 'another_view');
}
There is much more stuff related to views; consult the manual.
Note that you will need to register such controller in the bootstrap file like (Im also including examples on how to register other things):
use Phalcon\Loader;
// registers namespaces and other classes
$loader = new Loader();
$loader->registerNamespaces([
'MyNameSpace\Controllers' => 'path/controllers/',
'MyNameSpace\Models' => 'path/models/',
'MyNameSpace\Views' => 'path/views/'
]);
$loader->register();
4) You will also need to register a few things for the views. In the bootstrap file
use Phalcon\Mvc\View;
$injector->setShared('view', function () {
$view = new View();
$view->setViewsDir('path/views/');
return $view;
});
And this, together with other things you will need to do, particularly in the bootstrap process, will get you started in sending requests to the controller and action/view defined in the routes.
Those were basic examples. There is much more that you will need to learn, because I only gave you a few pieces to get you started. So here are some links that can explain more. Remember, there are several different ways to achieve the same thing in Phalcon.
Bootstrapping:
https://docs.phalconphp.com/en/3.2/di
https://docs.phalconphp.com/en/3.2/loader
https://docs.phalconphp.com/en/3.2/dispatcher
Routing: https://docs.phalconphp.com/en/3.2/routing
Controllers: https://docs.phalconphp.com/en/3.2/controllers
More on Views (from registering to passing data to them, to templating and more): https://docs.phalconphp.com/en/3.2/views
And a simple tutorial to teach you some basic things: https://docs.phalconphp.com/en/3.2/tutorial-rest
The application begins with the routing stage. From there you grab the controller and action from the router, and feed it to the dispatcher. You set the view then call the execute the dispatcher so it access your controller's action. From there you create a new response object and set its contents equal to the view requests, and finally send the response to the client's browser -- both the content and the headers. It's a good idea to do this through Phalcon rather than echoing directly or using PHP's header(), so it's only done at the moment you call $response->send(); This is best practice because it allows you to create tests, such as in phpunit, so you can test for the existence of headers, or content, while moving off to the next response and header without actually sending anything so you can test stuff. Same idea with exit; in code, is best to avoid so you can write tests and move on to the next test without your tests aborting on the first test due to the existence of exit.
As far as how the Phalcon application works, and in what steps, it's much easier to follow the flow by looking at manual bootstrapping:
https://docs.phalconphp.com/en/3.2/application#manual-bootstrapping
At the heart of Phalcon is the DI, the Dependency Injection container. This allows you to create services, and store them on the DI so services can access each other. You can create your own services and store them under your own name on the DI, there's nothing special about the names used. However depending on the areas of Phalcon you used, certain services on the DI are assumed like "db" for interacting with your database. Note services can be set as either shared or not shared on the DI. Shared means it implements singleton and keeps the object alive for all calls afterwards. If you use getShared, it does a similar thing even if it wasn't initially a shared service. The getShared method is considered bad practice and the Phalcon team is talking about removing the method in future Phalcon versions. Please rely on setShared instead.
Regarding multiple views, you can start with $this->view->disable(); from within the controller. This allows you to disable a view so you don't get any content generated to begin with from within a controller so you can follow how views work from within controllers.
Phalcon assumes every controller has a matching view under /someController/someView followed by whatever extension you registered on the view, which defaults to .volt but can also be set to use .phtml or .php.
These two correspond to:
Phalcon\Mvc\View\Engine\Php and Phalcon\Mvc\View\Engine\Volt
Note that you DON'T specify the extension when looking for a template to render, Phalcon adds this for you
Phalcon also uses a root view template index.volt, if it exists, for all interactions with the view so you can use things like the same doctype for all responses, making your life easier.
Phalcon also offers you partials, so from within a view you can render a partial like breadcrumbs, or a header or footer which you'd otherwise be copy-pasting into each template. This allows you to manage all pages from the same template so you're not repeating yourself.
As far as which view class you use within Phalcon, there's two main choices:
Phalcon\Mvc\View and Phalcon\Mvc\View\Simple
While similar, Phalcon\Mvc\View gives you a multiple level hierarchy as described before with a main template, and a controller-action based template as well as some other fancy features. As far as Phalcon\Mvc\View\Simple, it's much more lightweight and is a single level.
You should be familiar with hierarchical rendering:
https://docs.phalconphp.com/en/3.2/views#hierarchical-rendering
The idea is with Phalcon\Mvc\View that you have a Main Layout (if this template exists) usually stored in /views/index.volt, which is used on every page so you can toss in your doctypes, the title (which you would set with a variable the view passed in), etc. You'd have a Controller Layout, which would be stored under /views/layouts.myController.volt and used for every action within a controller (if this template exists), finally you'd have the Action Layout which is used for the specific action of the controller in /views/myController/myAction.volt.
There are all types of ways you can break from Phalcon's default behavior. You can do the earlier stated $this->view->disable(); so you can do everything manually yourself so Phalcon doesn't assume anything about the view template. You can also use ->pick to pick which template to use if it's going to be different than the controller and action it's ran in.
You can also return a response object from within a controller and Phalcon will not try to render the templates and use the response object instead.
For example you might want to do:
return $this->response->redirect('index/index');
This would redirect the user's browser to said page. You could also do a forward instead which would be used internally within Phalcon to access a different controller and/or action.
You can config the directory the views are stored with setViewsDir. You can also do this from within the controller itself, or even within the view as late as you want, if you have some exceptions due to a goofy directory structure.
You can do things like use $this->view->setTemplateBefore('common') or $this->view->setTemplateAfter('common'); so you can have intermediate templates.
At the heart of the view hierarchy is <?php echo $this->getContent(); ?> or {{ content() }} if you're using Volt. Even if you're using Volt, it gets parsed by Phalcon and generates the PHP version with $this->getContent(), storing it in your /cache/ directory, before it is executed.
The idea with "template before" is that it's optional if you need another layer of hierarchy between your main template and your controller template. Same idea with "template after" etc. I would advise against using template before and after as they are confusing and partials are better suited for the task.
It all depends on how you want to organize your application structure.
Note you can also swap between your main template to another main template if you need to swap anything major. You could also just toss in an "if" statement into your main template to decide what to do based on some condition, etc.
With all that said, you should be able to read the documentation and make better sense of how to utilize it:
https://docs.phalconphp.com/en/3.2/api/Phalcon_Mvc_View
How do you add, or get, variables using Yii::$app->user->something?
I don't want to get them through identity. I want to make specific ones accessible through user.
For example, I want to get the current logged in user's username and name:
\Yii::$app->user->username
or
\Yii::$app->user->name
While other topics here on SO offer a chincy hack solution, or to simply fetch it through Yii::$app->user->identity, I am going to show you how to do it properly. You can bend Yii2 to your will, by virtually extending everything!
I normally work in the "advanced" app, but I am going to just use the "basic" app as a reference for everyone. I assume those who are using the "advanced" app know the differences and understand namespaces.
Namespace Crash Course: In basic, you have namespace app\path\to. This is where the file is located. For example, you have ContactForm.php located in the app root models directory, so it's namespace is app\models. In advanced, you have multiple apps (frontend & backend), so the app root is different, thus you have frontend as one app root, and backend as another. The models directory in the advanced app is at "frontend/models" and it's namespace is frontend\models. -- So anywhere you see "app" in the namespace, you can replace it with frontend or backend for the "advanced" template.
Solution
First, we need to modify our app's config file to override the default User component's class. I will leave out anything not related, so you keep your identityClass defined, I just am not showing it. We are simply adding to, not replacing, the config. Also, where I show frontend/something, the same can be done for backend/something. I just am not going to repeat both over and over again...
Basic (config/web.php) -or- Advanced (frontend/config/main.php)
'components' => [
'user' => [
'class' => 'app\components\User', // extend User component
],
],
Remember: If you are using the advanced template, replace app with frontend or backend!
I also want to point out, that you will need to create the "components" directory if you haven't done so. This is where many people add their custom Classes. Whether it be your own helper class, or extending a base Yii component (such as extending yii\web\Controller).
Create a new file in your components directory named User.php. In that file, place the following code:
<?php
namespace app\components;
use Yii;
/**
* Extended yii\web\User
*
* This allows us to do "Yii::$app->user->something" by adding getters
* like "public function getSomething()"
*
* So we can use variables and functions directly in `Yii::$app->user`
*/
class User extends \yii\web\User
{
public function getUsername()
{
return \Yii::$app->user->identity->username;
}
public function getName()
{
return \Yii::$app->user->identity->name;
}
}
Note: username and name must be valid column names in your database's user table!
So now we can use this in our controller, view, anywhere.. Let's test it out. In views/site/index.php, add this to the bottom of the page:
<?= \Yii::$app->user->name ?>
<?= \Yii::$app->user->username ?>
If all is good, and you have values in the user table for name and username, they should be printed on to the page :)
I created Phalcon PHP app,
I have 3 different user profiles: (ID: 1) Administrators, (ID: 2) Accountants and (ID: 3) Warehouses.
I want my app able to render views based on those profiles, for example
controllerName/index.1.volt //for Administrators
controllerName/index.2.volt //for Accountants
controllerName/index.3.volt //for Warehouses
but when those files weren't found, my app will fallback to:
controllerName/index.volt
How do I accomplish that?
One approach, although messy, would be to use controlerName/index.volt as the "landing page" then from there check an if-statement to decide what the user's role is. Then from the if-statement use a partial like {{ partial("index.1.volt") }} but you'd need to hard-code this for every controller... yuck...
A good solution which I'd recommend, though, would be to make use of the View's exists method to check if the view exists from within your controller. The idea is you pass this method the string path to the view file you're looking for but omitting the extension. The reason you omit the extension is because Phalcon allows you to use multiple rendering engines so an application using a mixture of .volt and .phtml would work.
Assuming you were using user roles something like this:
define('GUEST_ROLE',0);
define('ADMIN_ROLE',1);
define('ACCOUNTANT_ROLE',2);
define('WAREHOUSE_ROLE',3);
(with the guest role with a value of 0) I would suggest having all your controllers extend a ControllerBase then define the following two methods in your ControllerBase:
public function getUserLevel()
{
if($this->session->has('userLevel'))
{
$userLevel=$this->session->get('userLevel');
return (int)$userLevel;
}else{
return 0;//default to guest
}
}
protected function initialize()
{
$controllerName=$this->dispatcher->getControllerName();
$actionName=$this->dispatcher->getActionName();
$userLevel=$this->getUserLevel();
if($this->view->exists("$controllerName/$actionName.$userLevel"))
{
$this->view->pick("$controllerName/$actionName.$userLevel");
}
//No reason to add an else, Phalcon defaults to "$controllerName/$actionName"
}
Just make sure, that if you ever need to define a custom "initialize" method for a specific controller which extends the ControllerBase, e.g. to add a title prefix to all pages related to the controller, that you call parent::initialize(); otherwise it won't get called. But that's only if you're going to be overriding the method.
Chances are you're already using a ControllerBase and doing similar logic already, if so, you'd need to edit your already existing "initialize" method and merge my code with yours.
Happy coding.
Laravel routing functionality allows you to name a resource and name a controller to go with it. I am new to Laravel and would like to know if anyone knows how to extend the resources method in the route class provided.
Basically say I have: (which works fine)
/invoices
But say I want:
/invoices/status/unpaid
How is this achievable?
To see the basics of what I am doing check:
http://laravel.com/docs/controllers#resource-controllers
Resource controllers tie you into a specific URLs, such as:
GET|POST /invoices
GET|PUT /invoices/{$id}
GET /invoices/create
and so on as documented.
Since, by convention, GET /invoices is used to list all invoices, you may want to add some filtering on that:
/invoices?status=unpaid - which you can then use in code
<?php
class InvoiceController extends BaseController {
public function index()
{
$status = Input::get('status');
// Continue with logic, pagination, etc
}
}
If you don't want to use filtering via a query string, in your case, you may be able to do something like:
// routes.php
Route::group(array('prefix' => 'invoice'), function()
{
Route::get('status/unpaid', 'InvoiceController#filter');
});
Route::resource('invoice', 'InvoiceController');
That might work as the order routes are created matter. The first route that matches will be the one used to fulfill the request.
Can you tell me how to use a controller for home page because i'm trying to put a model's data in home.ctp (homepage view) with
<?php $this->user->find() ?>but it returns
Notice (8): Undefined property:
View::$user [APP\views\pages\home.ctp,
line 1]
You should check out the cookbook; it has some solid CakePHP tutorials, at http://book.cakephp.org/
You haven't really provided alot of information, but my guess is your Controller uses a model 'User', and you're putting $this->user->find() in your view, when it should be in your controller. In your controller's action you'll want/need to do something like this...
Users_Controller extends AppController {
function index() {
$arrayOfUsers = $this->User->find(...);
$this->set('users', $arrayOfUsers);
}
}
You can then - in your View - access 'users' like so:
pre($users);
... since you used the Controller method set() to send a variable $users to the view.
All you really need to do is create a new controller if that's the direction you want to go. If this is the only statement you have that requires data access, it might be worth faking it in only this method of the PagesController. For example, one of my projects' homepages is 99% static save for a list of featured events. Rather than move everything out to a new controller or even loading my Event model for the entire PagesController (where it's not needed), I just applied this solution in PagesController::home():
$attractions = ClassRegistry::init ( 'Attraction' )->featured ( 10 );
Works great. If your page is more dynamic than mine, though, it may well be worth routing your homepage through a different controller (one that is more closely related to the data being displayed).
The default controller for the home page i.e home.ctp view, is located in /cake/libs/controller/pages_controller.php. This controller can be overriden by placing a copy in the controllers directory of your application as /app/controllers/pages_controller.php. You can then modify that controller as deem fit i.e. use models, inject variables to be used in the home page etc