I need to save the model id that is being saved and avoid another save while it's saving it, but I don't know how to persist data between requests and access it later.
JAVASCRIPT
Works perfectly well, but I can't do the same in LARAVEL 9.
// MODELS THAT ARE BEING UPDATED.
const models = {}
function handleRequest(req) {
if (models[req.model_id]) {
return
}
models[req.model_id] = true
// UPDATE MODEL.
models[req.model_id] = false
}
LARAVEL 9
Doesn't work as expected.
<?php
use App\Http\Controllers\Controller;
// MODELS THAT ARE BEING UPDATED.
$models = [];
class MyController extends Controller
{
public function index(Request $request)
{
if ($models[$model_id]) {
return;
}
$models[$model_id] = true;
// UPDATE MODEL.
$models[$model_id] = false;
}
}
Your problem is that you are trying to compare different entities in JS, you use a Function, and in Laravel you have a Class, these are different concepts and mechanics of work
You can make a property inside a class
<?php
use App\Http\Controllers\Controller;
class MyController extends Controller
{
// MODELS THAT ARE BEING UPDATED.
public $models = [];
public function index(Request $request)
{
if ($this->models[$model_id]) {
return;
}
$this->models[$model_id] = true;
// UPDATE MODEL.
$this->models[$model_id] = false;
}
}
You might consider using sessions:
session(['model_id' => 123]); // save data to session
$model_id = session('model_id'); // get data from session
Related
I'm building application in Phalcon PHP where I have database with access from website and API.
In normal website I would create MVC like here:
- app
-- controllers
-- models
-- views
- public
but I have problem with duplicate code for API and Web.
Sample code:
class Users extends Model {
// ...
protected $id;
protected $username;
protected $email;
// setters and getters, validation
}
class UserController extends ControllerBase {
// ...
public function loginAction() {
if ($this->request->isPost()) {
// ... get post
// check login is correct
// create session
// redirect
}
$this->view->var = $var;
}
}
class ApiController extends ControllerBase {
// ...
public function loginAction() {
if ($this->request->isPost() //or put) {
$json = $this->request->getJsonRawBody();
// ... get json
// check login is correct
// create session
}
$response->setStatusCode('2xx/4xx', 'msg');
$response->setJsonContent([
'status' => 'OK / ERROR',
'message' => '$msg / $ex->getMessage()'
]);
}
}
Now I would create class with logic for check is user data correct.
I think about class like this:
class MyClass extends ParentClass {
public function login($username, $password) {
$user = Users::findFirstByEmail($email);
if ($user->password === hash($password)) {
$successLogin = new UserSuccessLogins();
$successLogin ->setId('id');
$successLogin ->setIpAddress('ip');
$successLogin ->save();
} else {
$failedLogin = new UserFailedLogins();
$failedLogin->setId('id');
$failedLogin->setIpAddress('ip');
$failedLogin->save();
}
}
}
And now I could use it in controllers like here:
class UserController extends ControllerBase {
public function loginAction() {
if ($this->request->isPost()) {
$c = new MyClass();
if ($c->login($username, $password)) {
// redirect
}
}
$this->view->var = $var;
}
}
class ApiController extends ControllerBase {
public function loginAction() {
if ($this->request->isPost() //or put) {
$c = new MyClass();
if ($c->login($username, $password)) {
// send json OK
} else {
// send json Error
}
}
}
}
What is best way for this? I don't want logic in model class.
I have read about Plugin and Component, but I don't know how create good self commented code.
You might be looking for Phalcon Multimodule, have a look at this example. Besides "Front-End" and "Back-End" modules, you can add "API" module.
OK, I'm going to extend my project with components like here:
-app
--components
--controllers
--models
--views
-public
Now, my code may looks like below:
use Phalcon\Mvc\Model;
class Users extends Model {
// ...
protected $id;
protected $username;
protected $email;
// setters and getters, validation
}
use Phalcon\Mvc\User\Component;
class UserComponent extends Component {
// class with access to dependecy injector
public login ($email, $password) {
$user = Users::findFirstByEmail($email);
// logic with setting session in $di
}
}
class UserController extends ControllerBase {
public function loginAction() {
if ($this->request->isPost()) {
$userComponent = new UserComponent();
if ($userComponent ->login($username, $password)) {
return $this->response->redirect($this->url->getBaseUri(), false, 301);
} else {
$this->flash->error('message');
}
}
// setting view variables if not post or login filed
$this->view->var = $var;
}
}
class ApiController extends ControllerBase {
public function loginAction() {
if ($this->request->isPost()) {
$userComponent = new UserComponent();
if ($userComponent ->login($username, $password)) {
//json OK
} else {
//json Error
}
}
}
}
If no one have better proposition I'll close this topic as solved in few days.
Your suggestion is a good option, however if you want to decouple and segregate responsibilities in a better way, you can try to use a service layer like in this example https://github.com/phalcon/mvc/tree/master/multiple-service-layer-model. Where you will have:
entities ( the models generated by phalcon)
repositories ( all the operations that requires fetching, updating or persisting data)
services (where the business logic is).
Whit this the call graph can be summarised as follow:
controllers -> services -> repositories -> entities
Note that the dependencies go in a single direction, nonetheless for simple tasks you can use a repo inside the controller directly o a entity inside the service, is up to you how hard or flexible your architecture will be.
I hope It is clear regards.
Laravel documentation suggests the following way to set up an eloquent model:
$user = user::with($conditions)->first();
What if I want to set up my eloquent model inside the model itself:
$user = new user();
$user->setup($conditions);
// class definition
class user extends Eloquent{
public function setup($conditions){
// load current object with table data
// something like
$this->where($conditions)->first();
// previous line output is dangling, is ok to assign it to $this variable?
}
}
If you're extending from Eloquent model, you may try the following approach. I assume you have a unique id column.
public function setup($conditions)
{
$model = self::with($conditions)->first();
if (! is_null($model)) {
$this->exists = true;
$this->forceFill(self::find($model->id)->toArray());
}
return $this;
}
Hope this solve your issue.
Pre-info: Laravel 5 is my first framework I've used besides our custom framework that we've created over the years. I'm still wrapping my head around the concepts but its mostly all there. I have page calls, authorization checks, form submission and db queries all working.
The issue: In the past I would create a new class "Access" and I'd call the function desired wherever I'd need to:
$access = Access::getAccessByAccount($accountID);
My hope is to do the same in laravel somehow and be able to call this public function from within a controller.. I just don't know how to call it and where to actually store the function.
Here is a sample of the function I'd like to call:
public function getAccessByAccount($accountID){
//Grab all access rights set to given account ID
$accessList = DB::table('element_access')
->join('element', 'element.id', '=', 'element_access.element_id')
->select('element.name as element', 'element_access.permission as permission')
->where('element_access.account_id', $accountID)
->get();
//Return $access[element] = permission list or false if no access rights are assigned to account ID
if(is_array($accessList)){
$access = array();
foreach($accessList as $item){
$access[$item->element] = $item->permission;
}
return $access;
}else{
return false;
}
}
Here is how I'd like to somehow be able to call it in a controller:
<?php namespace App\Http\Controllers\Portal\Admin;
use App\Http\Controllers\Controller;
class AdminController extends Controller {
public function showAdminDashboard(){
$access = Access::getAccessByAccount(Auth::id());
if($access['admin-dashboard'] == 'r'){
return view('portal.admin.dashboard');
}
}
}
EDITS:
Here is the solution I came up with with the help of the checked solution.
Created new file: app\Library\Access.php
<?php namespace App\Library;
use DB;
class Access{
public function getElementAccessByAccount($accountID){
//Grab all access rights set to given account ID
return DB::table('element_access')
->join('element', 'element.id', '=', 'element_access.element_id')
->select('element.name as element', 'element_access.permission as permission')
->where('element_access.account_id', $accountID)
->get();
}
}
To call the function:
$access = new \App\Library\Access;
$accessList = $access->getElementAccessByAccount(Auth::id());
If I was in your shoes, I would store all classes of custom functionalities to the app/services directory.
Access class
<?php namespace App\Services;
class Access
{
public static function getAccessByAccount($accountID) {
//Grab all access rights set to given account ID
$accessList = \DB::table('element_access')
->join('element', 'element.id', '=', 'element_access.element_id')
->select('element.name as element', 'element_access.permission as permission')
->where('element_access.account_id', $accountID)
->get();
//Return $access[element] = permission list or false if no access rights are assigned to account ID
if (is_array($accessList)) {
$access = array();
foreach($accessList as $item){
$access[$item['element']] = $item['permission'];
}
return $access;
} else{
return false;
}
}
}
Controller
<?php namespace App\Http\Controllers\Portal\Admin;
use App\Http\Controllers\Controller;
class AdminController extends Controller
{
public function showAdminDashboard() {
$access = \App\Services\Access::getAccessByAccount(\Auth::id());
if($access['admin-dashboard'] == 'r') {
return view('portal.admin.dashboard');
}
}
}
Before anyone asks, I've looked into CRUD generators and I know all about the Laravel Resource routes, but that's not exactly what I'm pulling for here.
What I'm looking to do is create one Route with a couple parameters, and one global class that (uses/extends?) the Model controller for simple CRUD operations. We have 20 or so Models and creating a Resource Controller for each table would be more time consuming than finding a way to create a global CRUD class to handle all "api" type calls and any ajax json request like a create / update / destroy statement.
So my question is what is the cleanest and best way to structure a class to handle all CRUD requests for every Model we have without having to have a resource controller for every model? I've tried researching this and can't seem to find any links except ones to CRUD generators and links describing the laravel Resource route.
The easiest way would be to do the following:
Add a route for your resource controller:
Route::resource('crud', 'CrudController', array('except' => array('create', 'edit')));
Create your crud controller
<?php namespace App\Http\Controllers;
use Illuminate\Routing\Controller;
use App\Models\User;
use App\Models\Product;
use Input;
class CrudController extends Controller
{
const MODEL_KEY = 'model';
protected $modelsMapping = [
'user' => User::class,
'product' => Product::class
];
protected function getModel() {
$modelKey = Input::get(static::MODEL_KEY);
if (array_key_exists($modelKey, $this->modelsMapping)) {
return $this->modelsMapping[$modelKey];
}
throw new \InvalidArgumentException('Invalid model');
}
public function index()
{
$model = $this->getModel();
return $model::all();
}
public function store()
{
$model = $this->getModel();
return $model::create(array_except(Input::all(), static::MODEL_KEY));
}
public function show($id)
{
$model = $this->getModel();
return $model::findOrFail($id);
}
public function update($id)
{
$model = $this->getModel();
$object = $model::findOrFail($id);
return $object->update(array_except(Input::all(), static::MODEL_KEY));
}
public function destroy($id)
{
$model = $this->getModel();
return $model::remove($id);
}
}
Use your new controller :) You have to pass the model parameter that will contain the model key - it must be one of the allowed models in the whitelist. E.g. if you want to get a User with id=5 do
GET /crud/5?model=user
Please keep in mind that it's as simple as possible, you might need to make the code more sophisticated to match your needs.
Please also keep in mind that this code has not been tested - let me know if you see any typos or have some other issues. I'll be more than happy to get it running for you.
Unless you want to implement CRUD manually, consider to integrate a ready-made datagrid such as phpGrid.
Check out integration walkthrough: http://phpgrid.com/example/phpgrid-laravel-5-twitter-bootstrap-3-integration/ No models are required and the code is minimum. It can almost do anything.
A basic working CRUD:
// in a controller
public function index()
{
$dg = new \C_DataGrid("SELECT * FROM orders", "orderNumber", "orders");
$dg->enable_edit("FORM", "CRUD");
$dg->display(false);
$grid = $dg -> get_display(true);
return view('dashboard', ['grid' => $grid]);
}
You need one generic class for all CRUD operations and there are many ways to achieve that and one rule for all may not fit but you may try the approach that I'm going to describe now. This is an abstract idea, you need to implement it, so at first, think the URI for all CRUD operations. In this case you must follow a convention and it could be something like this:
example.com/user/{id?} // get all or one by id (if id is available in the URI)
example.com/user/create // Show an empty form
example.com/user/edit/10 // Show a form populated with User model
example.com/user/save // Create a new User
example.com/user/save/10 // Update an existing User
example.com/user/delete/10 // Delete an existing User
In ths case the user could be something else to specify the name of the model for example, example.com/product/create and keeping that on mind, you need to declare routes as given below:
Route::get('/{model}/{id?}', 'CrudController#read');
Route::get('/{model}/create', 'CrudController#create');
Route::get('/{model}/edit/{id}', 'CrudController#edit');
Route::post('/{model}/save/{id?}', 'CrudController#save');
Route::post('/{model}/delete/{id}', 'CrudController#delete');
Now, in your app\Providers\RouteServiceProvider.php file modify the boot method and make it look like this:
public function boot(Router $router)
{
$model = null;
$router->bind('model', function($modelName) use (&$model, &$router)
{
$model = app('\App\User\\'.ucfirst($modelName));
if($model)
{
if($id = $router->input('id'))
{
$model = $model->find($id);
}
return $model ?: abort(404);
}
});
parent::boot($router);
}
Then declare your CrudController as given below:
class CrudController extends Controller
{
protected $request = null;
public function __construct(Request $request)
{
$this->request = $request;
}
public function read($model)
{
return $model->exists ? $model : $model->all();
}
// Show either an empty form or a form
// populated with the given model atts
public function createOrEdit($model)
{
$classNameArray = explode('\\', get_class($model));
$className = strtolower(array_pop($classNameArray));
$view = view($className . '.form');
$view->formAction = "$className/save";
if(is_object($model) && $model->exists)
{
$view->model = $model;
$view->formAction .= "/{$model->id}";
}
return $view;
}
public function save($model)
{
// Validation required so do it
// Make sure each Model has $fillable specified
return $this->model->fill($this->request)->save();
}
public function delete($model)
{
return $this->model->delete();
}
}
Since same form is used to creating and updating a model, use something like this to create a form:
<form action="{{url($formAction)}}" method="POST">
<input
type="text"
class="form-control"
name="first_name" value="{{old('first_name', #$model->first_name)}}"
/>
<input type="Submit" value="Submit" />
{!!csrf_field()!!}
</form>
Remember that, each form should be in a directory corresponding to the model, for user add/edit, form should be in views/user/form.blade.php and for product model use views/product/form.blade.php and so on.
This will work and don't forget to add validation before saving a model and validation could be done inside the model using model events or however you want. This is just an idea but probably not the best way to it.
I am developing a joomla 2.5 component where I need to pass data from controller to model. The controller is receiving data from url. I find that controller is getting the value properly. Now I need to move that value to model from controller. From different post I have found a snippet of code for controller like below.
$datevalue = JRequest::getVar('day',$day); //receiving value from view
$item = JRequest::setVar('day',$datevalue); //setting variable
$model =& $this->getModel('WeeklyProgram'); //assign model
$model->setState('dayVar', $item); // assign value for model
The problem is that I don't know how to receive this value 'dayVar' from model. Can anybody help me on this issue? Thanks.
Use following things
In Modal
class CommunityModelCevent extends JCCModel
{
var $membersCount = null;
function getMembersCount($value) {
$this->membersCount = $value // set your value here 15
// Now you can access this variable into model
}
}
In controller
$ceventModel = CFactory::getModel( 'cevent' );
$membersCount = $ceventModel->getMembersCount(15);
You can do like this . First you make get and set function in the model.Second load the model in the controller and simply pass the values to setter function.Example as follows:
updateratings.php---this is my model
class RatingManagerModelUpdateRatings extends JModelLegacy
{
public $data;
public function get_data(){
$data=$this->data;
return $data;
}
public function set_data($data){
$this->data=$data;
}
}
Controller.php
class RatingManagerController extends JControllerLegacy
{
public function save_ratings(){
$tips = JRequest::getVar('tips'); //get data from front end form
$model = $this->getModel('UpdateRatings'); //load UpdateRatings model
$model->set_data($tips); //update setter function of model
$res=$model->get_data(); // retrieve getter function
//print_r($res);
}
}