Make model dynamically using user supplied model name in laravel - php

I am stuck with it and i couldn't find any appropriate solution for this. what i want to achieve is in my admin panel based upon check box value i want to change active status of specific control and update database value using ajax. I want to make a common ajax function in controller to avoid repeatedly writing same ajax function for other controls like menu manager content manager, documents manager etc.So i want to send Model name to the ajax controller so that same function can be used. Ajax call is working perfectly but couldn't make appropriate models. for example:
$m = new App\Model.$request->model OR $m = 'App\Model\'.$request->model (adding last \ gives an error) or answer provided in Dynamically use model in laravel is not helping either. Is there any better ways if yes please suggest. I can do this below but it is hardcoded so i want to make model dynamic models
if($request->model ==='Menu')
$model = new \App\Http\Models\Menu;
else if($request->model === 'News')
$this->model = new \App\Http\Models\News;
else if($request->model === 'Document')
$this->model = new \App\Http\Models\Document;
Thankyou !!!

You can just use:
$modelName = '\\App\\Http\\Models\\'.$request->model;
$this->model = new $modelName;
However you should add validation to make sure only some allowed models would be used or at least do something like this:
if (! in_array($request->model, ['Menu', 'News', 'Document']))
{
throw new \Exception('Invalid model');
}
$modelName = '\\App\\Http\\Models\\'.$request->model;
$this->model = new $modelName;
This is because you don't want to expose probably all the models data for security reasons.

Try the below code
if($request->model){
$m = '\App'. '\' .$request->model;
//other code
}

Related

How to set & lock SiteTree in Silverstripe 4?

We're working on a project where Silverstripe acts as headless CMS with a single API data point.
The content is all from non-SiteTree dataobjects & the API data point is the only SiteTree record we need.
What is the best way of making sure the API data point exists & it is the only SiteTree record on each /dev/build. We could then disable PagesAdmin in LeftAndMain so users couldn't edit/ break it.
I've found code here which would look a bit like this for our case:
$api = new API/AccessPoint();
$api->Title = 'API';
$api->URLSegment = 'home';
$api->ShowInMenus = '1';
$api->Version = '1';
$api->ParentID = '0';
$api->write();
$api->doRestoreToStage();
But I'm not sure of the correct approach for making sure that a dev\build removes all the other pages & creates 1 record of this page.
Any help would be appreciated.
You can use requireDefaultRecords() in your DataObject to create records on a dev/build. You'll need to do some kind of check before creating a new one though, e.g.
public function requireDefaultRecords()
{
parent::requireDefaultRecords();
if (AccessPoint::get()->filter('URLSegment', 'home')->exists()) {
return;
}
// create your new object here
}

Need More Clarification on Events YII2

Trying to learn events in Yii 2. I found a few resources. The link I got more attention is here.
How to use events in yii2?
In the first comment itself he explains with an example. Say for an instance we have 10 things to do after registration - events comes handy in that situation.
Calling that function is a big deal? The same thing is happening inside the model init method:
$this->on(self::EVENT_NEW_USER, [$this, 'sendMail']);
$this->on(self::EVENT_NEW_USER, [$this, 'notification']);
My question is what is the point of using events? How should I get full benefit of using them. Please note this question is purely a part of learning Yii 2. Please explain with an example. Thanks in advance.
I use triggering events for written (by default) events like before validation or before deletion. Here's an example why such things are good.
Imagine that you have some users. And some users (administrators, for example) can edit other users. But you want to make sure that specific rules are being followed (let's take this: Only main administrator can create new users and main administrator cannot be deleted). Then what you can do is use these written default events.
In User model (assuming User models holds all users) you can write init() and all additional methods you have defined in init():
public function init()
{
$this->on(self::EVENT_BEFORE_DELETE, [$this, 'deletionProcess']);
$this->on(self::EVENT_BEFORE_INSERT, [$this, 'insertionProcess']);
parent::init();
}
public function deletionProcess()
{
// Operations that are handled before deleting user, for example:
if ($this->id == 1) {
throw new HttpException('You cannot delete main administrator!');
}
}
public function insertionProcess()
{
// Operations that are handled before inserting new row, for example:
if (Yii::$app->user->identity->id != 1) {
throw new HttpException('Only the main administrator can create new users!');
}
}
Constants like self::EVENT_BEFORE_DELETE are already defined and, as the name suggests, this one is triggered before deleting a row.
Now in any controller we can write an example that triggers both events:
public function actionIndex()
{
$model = new User();
$model->scenario = User::SCENARIO_INSERT;
$model->name = "Paul";
$model->save(); // `EVENT_BEFORE_INSERT` will be triggered
$model2 = User::findOne(2);
$model2->delete(); // `EVENT_BEFORE_DELETE` will be trigerred
// Something else
}

Laravel Auth User add custom data

We are currently working on an application with a Google Login with Laravel with Socialite. We have a Auth user who gets a permission number ex. 264. We have made a function which returns an array with all binary numbers this permission number is made off.
Because calling this function every single time a page loads may be kinda heavy, we thought of adding this once when the Auth::user() is created. We thought of adding a custom constructor in the Model, but we can't make it work.
function __construct($attributes = array()) {
parent::__construct($attributes);
$this->permissionsArray = PermissionHelper::permissionConverter($this->permissions);
}
But we can't get it to work, $this doesn't have values when calling this function.
TLDR;
Directly after making the Auth user I want to call the permissionConverter function and save the data to the user so we can use it more often. Any suggestions on how to do this?
EDIT: I checked all answers out today, succeeded with one of them, but I assumed Laravel put the authenticated user in the SESSION or something. I found out it doesn't and it gets all the data from the database every request. We couldn't do what we requested for unfortunately. So I just had to refactor the script and make it as efficient as possible (although it became a bit less readable for less experienced programmers).
Thanks for the help :D
Maybe you can use this solution ? https://stackoverflow.com/a/25949698/7065748
Create a on the User Eloquent model a boot method with
class User extends BaseModel {
public static function boot() {
static::creating(function($model) {
$model->permissionsArray = PermissionHelper::permissionConverter($model->permissions);
});
// do the same for update (updating) if necessary
}
}
Can't you just use this method ?
If new user:
$user = new User(); // or User:create(['...']) directly
$user->name = 'toto';
// and all other data
or
$user = Auth::user();
then
$user->permissionsArray = PermissionHelper::permissionConverter($user->permissions);
$user->save();

How to reload/refresh model from database in Laravel?

In some of my tests, I have a user model I have created and I run some methods that need to save certain attributes. In rails, I would typically call something like user.reload which would repopulate the attributes from the database.
Is there a way in laravel to do that? I read through the api and couldn't find a method for it: http://laravel.com/api/4.1/Illuminate/Database/Eloquent/Model.html Any ideas on the "right" way to do this?
There was a commit submitted to the 4.0 branch made in August to add a reload() method, but so far it hasn't been merged with the newer Laravel branches.
But... Laravel 5 is providing a "fresh()" method that will return a new instance of the current model. Once you're using Laravel 5.0 or newer, you can reload a model like this:
$model = $model->fresh();
Note that fresh() doesn't directly update your existing $model, it just returns a new instance, so that's why we need to use "$model = ". It also accepts a parameter which is an array of relations that you want it to eager load.
If you aren't yet using Laravel 5 but you want the same functionality, you can add this method to your model(s):
public function fresh(array $with = array())
{
$key = $this->getKeyName();
return $this->exists ? static::with($with)->where($key, $this->getKey())->first() : null;
}
Update: If you're using Laravel 5.4.24 or newer, there is now a $model->refresh() method that you can use to refresh the object's attributes and relationships in place rather than fetching a new object like fresh() does. See Jeff Puckett answer for more specifics on that.
Thanks to PR#19174 available since 5.4.24 is the refresh method.
$model->refresh();
This way you don't have to deal with reassignment as is shown in other answers with the fresh method, which is generally not helpful if you want to refresh a model that's been passed into another method because the variable assignment will be out of scope for the calling contexts to use later.
refresh() is a mutable operation: It will reload the current model instance from the database.
fresh() is an immutable operation: It returns a new model instance from the database. It doesn't affect the current instance.
// Database state:
$user=User::create([
'name' => 'John',
]);
// Model (memory) state:
$user->name = 'Sarah';
$user2 = $user->fresh();
// $user->name => 'Sarah';
// $user2->name => 'John'
$user->refresh();
// $user->name => 'John'
I can't see it either. Looks like you'll have to:
$model = $model->find($model->id);
You can also create one yourself:
public function reload()
{
$instance = new static;
$instance = $instance->newQuery()->find($this->{$this->primaryKey});
$this->attributes = $instance->attributes;
$this->original = $instance->original;
}
Just tested it here and it looks it works, not sure how far this goes, though, Eloquen is a pretty big class.
I believe #Antonio' answer is the most correct, but depending on the use case, you could also use a combination of $model->setRawAttributes and $model->getAttributes.
$users = User::all();
foreach($users as $user)
{
$rawAttributes = $user->getAttributes();
// manipulate user as required
// ..
// Once done, return attribute state
$user->setRawAttributes($rawAttributes);
}
The primary downside to this is that you're only "reloading" the data attributes, not any relationships you've altered, etc. That might also be considered the plus side.
EDIT
As of L5 - fresh() is the way to go

Non-public accessible function in CakePHP

I have built a simple Notification system in my Cake app that I want to have a function that will create a new notification when I call a certain method. Because this is not something the user would actually access directly and is only database logic I have put it in the Notification model like so:
class Notification extends AppModel
{
public $name = 'Notification';
public function createNotification($userId, $content, $url)
{
$this->create();
$this->request->data['Notification']['user_id'] = $userId;
$this->request->data['Notification']['content'] = $content;
$this->request->data['Notification']['url'] = $url;
$result = $this->save($this->request->data);
if ($result)
{
$this->saveField('datetime', date('Y-m-d H:i:s'));
$this->saveField('status', 0);
}
}
}
And then whenever I want to create a notification within my app I just do:
$this->Notification->createNotification($userId,'Test','Test');
However this doesn't work! The controller is talking to the model fine, but it doesn't create the row in the database... I'm not sure why... but it would seem I'm doing this wrong by just doing all the code in the model and then calling it across the app.
Edit: Based on answers and comments below, I have tried the following the code to create a protected method in my notifications controller:
protected function _createNotification($userId, $content, $url)
{
$this->Notification->create();
$this->request->data['Notification']['user_id'] = $userId;
$this->request->data['Notification']['content'] = $content;
$this->request->data['Notification']['url'] = $url;
$result = $this->save($this->request->data);
if ($result)
{
$this->saveField('datetime', date('Y-m-d H:i:s'));
$this->saveField('status', 0);
}
}
Now the thing that is stumping me still (apologies if this is quite simple to others, but I have not used protected methods in CakePHP before) is how do I then call this from another controller? So for example If have a method in my PostsController and want to create a notification on successful save, how would I do this?
I thought about in my PostsController add method:
if($this->save($this->request-data){
$this->Notification->_createNotification($userId,'Test','Test');
}
But being protected I wouldn't be able to access the method from outside of the NotificationsController. Also I'm using the same syntax as if I was calling a function from a model so again it doesn't feel right.
Hopefully someone can help me out and get me back on track as this is a new area to me.
the controller should pass all data to the model
$this->createNotification($this->request->data);
the model then can use the data:
public function createNotification(array $data) {
$key = $data[$this->alias]['key'];
$data[...] = ...;
$this->create();
return $this->save($data);
}
you never ever try to access the controller (and/or its request object) from within a model.
you can also invoke the method from other models, of course:
public function otherModelsMethod() {
$this->Notification = ClassRegistry::init('Notification');
$data = array(
'Notification' => array(...)
);
$this->Notification->createNotification($data);
}
and you can make your methods verbose, but that usually makes it harder to read/understand/maintain with more and more arguments:
public function createNotification($userId, $content, $url) {
$data = array();
// assign the vars to $data
$data['user_id'] = $userId;
...
$this->create();
return $this->save($data);
}
so this is often not the cake way..
Methods in a model are not "publicly accessible" by definition. A user cannot call or invoke a method in a model. A user can only cause a controller action to be initiated, never anything in the model. If you don't call your model method from any controller, it's never going to be invoked. So forget about the "non-public" part of the question.
Your problem is that you're working in the model as if you were in a controller. There is no request object in a model. You just pass a data array into the model method and save that array. No need for $this->request. Just make a regular array(), put the data that was passed by the controller in there and save it.
The whole approach is totally wrong in the MVC context IMO and screams for the use of the CakePHP event system. Because what you want is in fact trigger some kind of event. Read http://book.cakephp.org/2.0/en/core-libraries/events.html
Trigger an Event and attach a global event listener that will listen for this kind of events and execute whatever it should do (save something to db) when an event happens. It's clean, flexible and extendible.
If you did a proper MVC stack for your app most, if not all, events aka notifications should be fired from within a model like when a post was saved successfully for example.
This is what I have ended up doing. While it certainly isn't glamorous. It works for what I want it to do and is a nice quick win as the notifications are only used in a few methods so I'm not creating a large amount of code that needs improving in the future.
First to create a notification I do the following:
$notificationContent = '<strong>'.$user['User']['username'].'</strong> has requested to be friends with you.';
$notificationUrl = Router::url(array('controller'=>'friends','action'=>'requests'));
$this->Notification->createNotification($friendId,$notificationContent,$notificationUrl);
Here I pass the content I want and the URL where the user can do something, in this case see the friend request they have been notified about. The url can be null if it's an information only notification.
The createNotification function is in the model only and looks like:
public function createNotification($userId, $content, $url = null)
{
$this->saveField('user_id',$userId);
$this->saveField('content',$content);
$this->saveField('url',$url);
$this->saveField('datetime', date('Y-m-d H:i:s'));
$this->saveField('status', 0);
}
This creates a new record in the table with the passed content, sets its status to 0 (which means unread) and the date it was created. The notification is then set as read when a user visits the notifications page.
Again this is most probably not an ideal solution to the problem outlined in this question... but it works and is easy to work with And may prove useful to others who are learning CakePHP who want to run functions from models when building prototype apps.
Remember nothing to stop you improving things in the future!
First of all, you can improve your last solution to do one save() (instead of 5) the following way:
public function createNotification($userId, $content, $url = null){
$data = array(
'user_id' => $userId,
'content' => $content,
'url' => $url,
'datetime' => date('Y-m-d H:i:s'),
'status' => 0
);
$this->create();
$this->save($data);
}
When I began programming CakePHP(1.3) more than a year ago I also had this problem.
(I wanted to use a function of a controller in any other controller.)
Because I didn't know/researched where to place code like this I've done it wrong for over a year in a very big project. Because the project is really really big I decided to leave it that way. This is what i do:
I add a function (without a view, underscored) to the app_controller.php:
class AppController extends Controller {
//........begin of controller..... skipped here
function _doSomething(){
//don't forget to load the used model
$this->loadModel('Notification');
//do ur magic (save or delete or find ;) )
$tadaaa = $this->Notification->find('first');
//return something
return $tadaaa;
}
}
This way you can access the function from your Notification controller and your Posts controller with:
$this->_doSomething();
I use this kind of functions to do things that have nothing to do with data submittance or reading, so i decided to keep them in the app_controller. In my project these functions are used to submit e-mails to users for example.. or post user actions to facebook from different controllers.
Hope I could make someone happy with this ;) but if you're planning to make a lot of these functions, it would be much better to place them in the model!

Categories