Saving from eloquent constructor in laravel - php

So, I'm currently working on a browser game in Laravel. So far I love the framework, but I haven't really got much experience, and I just can't get this to work.
Basically I'm trying to update all users whenever they are instantieted, as there is no reason update them when they are not used. But calling this function from the constructor doesn't update the user, it only works when I call the function outside the constructor.
Have I missed anything, or is it just not possible?
Thanks in advance!
<?php
class User extends Eloquent implements UserInterface, RemindableInterface {
use UserTrait, RemindableTrait;
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'users';
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = array('password', 'remember_token');
public function __construct($arguments = array())
{
parent::__construct($arguments);
$this->updateHp();
}
public function updateHp()
{
$this->hp_last = time();
$this->save();
}
}

Eloquent is a static class, data is fetched on query (find, first, get) and when you create a model you have just a blank model, with no data on it. This is, as example, the point where you have some data available:
public static function find($id, $columns = array('*'))
{
if (is_array($id) && empty($id)) return new Collection;
$instance = new static;
return $instance->newQuery()->find($id, $columns);
}
Before one of those query methods, you have void.
So you probably cannot do that during __construct because your model is still blank (all nulls). This is what you can do to make it, somehow, automatic:
First, during boot, create some creating and updating listeners:
public static function boot()
{
static::creating(function($user)
{
$user->updateHp($user);
});
static::updating(function($user)
{
$user->updateHp($user);
});
parent::boot();
}
public function updateHp()
{
$this->hp_last = time();
$this->save();
}
Then, every time you save() a model it will, before saving, fire your method:
$user = User::where('email', 'acr#antoniocarlosribeiro.com')->first();
$user->activation_code = Uuid::uuid4();
$user->save();
If you want to make it somehow automatic for all your users. You can hook it to a login event. Add this code to your global.php file:
Event::listen('user.logged.in', function($user)
{
$user->updateHp();
})
Then in your login method you'll have to:
if ($user = Auth::attempt($credentials))
{
Event::fire('user.logged.in', array($user));
}

In my opinion you shouldn't do that. If you use the code:
$user = new User();
you would like to be run:
$this->hp_last = time();
$this->save();
and what exactly should happen in this case? New user without id should be created with property hp_last ?
I think that's not the best idea.
You should leave it in the function then you can use:
$user = new User();
$user->find(1);
$user->updateHp();
That makes much more sense for me.

Related

Laravel 5.6 database operations in model

I have a controller called UserController, in that controller i am inserting a row of data to table "user" like this
$user = new UsersModel();
$user->first_name = $request->input('firstName');
$user->last_name = $request->input('lastName');
$user->about = $request->input('userAbout');
$user->join_date = date('Y-m-d');
$user->save();
My Question is, can i write this in my model called UsersModel???
Something Like,
( The insertData($data) is called from controller class.)
class UsersModel extends Model
{
protected $fillable = ['id','first_name','last_name','about','image','join_date','created_at','updated_at'];
protected $table = 'users';
public function insertData($data) {
// nb: $data contains values of fileds
// insert operation
//also return some values
}
}
You don't need to define your own function when you can already do it through Eloquent by simply calling the static method create magically:
$ref = UsersModel::create([
'col' => 'val'
]);
where $ref contains the information about the created data.
No need to reinvent the wheel in this instance.
However, your own custom method is possible too, make sure your function is defined as static to allow you to use without an object reference.
Yes you can
you need to call the function from the controller like this
$data = ['YOUR ARRAY'];
$this->usersModel = new UsersModel();
$this->usersModel->insertData($data);
You can also do with static function
In Model
public static function insertData($data) {
In Controller
UsersModel::insertData($data);
Insert function
UsersModel::insert($data);

How can I make a specific function on action page to handle the form data?

I have a models.php page that contains the specification of form for a specific model.
models.php
$books = [
['Book Name', 'text' ],
['Author', 'text']
];
$vegetables = [
['Name', 'text'],
['Photo', 'file']
]
Now this page is accessed by an admin.php page, which generate an appropriate HTML form on the basis of the given name and input type.
I want to fill the form and send the data into a handle.php and handle the data with the specific function to fill the data into appropriate table.
handle.php
function books(){
// this will fill the details into table of books.
INSERT INTO BOOKS
name = $_POST['book_name']
author $_POST['author']
}
function vegetables(){
// this will fill the details into table of vegetables.
INSERT INTO VEGETABLES
name = $_POST['book_name']
photo = $_FILE['photo']
}
(If there's any other better way of doing this, so please mention, I'll do that way and delete my question.)
Here's my suggestion. As stated in the comments, this is just my way to do such things, it's not necessarily the best solution for every situation.
I have a base model, that defines all methods all model need to have in common. Here's a very simplified version:
class Model {
public $modelName = 'default';
public $id = null;
private $fields = [];
private $tableName = 'default';
private $tableDefinition = [];
private $idField = 'id';
public function insert($dataset) {
// do some database magic by using $this->fields, or $this->tableDefinition
$sql = "INSERT into {$this->tableName} ...";
...
return $id;
}
public function update($id, $dataset) {
// do some more database magic by using $this->fields, or $this->tableDefinition
}
// many more methods. To get data, delete, sort, ..
//...
}
Every model now extends this base model class and sets it's specific params, maybe even overrides some methods or adds special ones:
class Books extends Model {
public $modelName = 'book';
private $fields = ['bookName','Author'];
private $tableName = 'BOOKS';
private $tableDefinition = [
['bookName','varchar'],
['Author','varchar']
];
// private $idField = 'id'; // you can ommit that, if it's the default.
}
If Vegetables behaves different you can simply override a method:
class Vegetables extends Model {
public $modelName = 'vegetable';
// set all other properties...
// override insert() for example
public function insert($dataset) {
// do something that doesn't comply with the standard procedure
}
}
Then in handle.php you can do something like this:
<?php
$modelName = $request; // get it from your form, your url, ..
// & verify this model(file) exists.
$model = new $modelName();
$model->insert($dataSet);
Make a base interface BaseModel.php
which would have the basic signatures of insertion , updation and selection
Make a derived class booksModel.php and vegetablesModel.php that would implement the BaseModel class.
In this way, you have made your code extendable. If there is some common functionality, you can make the base class as Abstract class.
abstract class BaseModel {
abstract function add($dataObject);
abstract function get($dataObject);
}
class BooksModel extends BaseModel {
public function add($dataObject) {
/* Implementation */
}
public function get($dataObject) {
/* Implementation */
}
}
class VegetableModel extends BaseModel {
public function add($dataObject) {
/* Implementation */
}
public function get($dataObject) {
/* Implementation */
}
}

In laravel how to pass extra data to mutators and accessors

What I'm trying to do is to append the comments of each article to the articles object, but the problem is that I need to request different number of comments each time.
and for some reason I need to use mutators for that, because some times I request 50 articles and I don't want to loop through the result and append the comments.
So is it possible to do something like the following and how to pass the extra argument.
This the Model:
class Article extends Model
{
protected $appends = ['user', 'comments', 'media'];
public function getCommentsAttribute($data, $maxNumberOfComments = 0)
{
// I need to set maxNumberOfComments
return $this->comments()->paginate($maxNumberOfComments);
}
}
Here is the controller:
class PostsController extends Controller
{
public function index()
{
//This will automatically append the comments to each article but I
//have no control over the number of comments
$posts = Post::user()->paginate(10);
return $posts;
}
}
What I don't want to do is:
class PostsController extends Controller
{
public function index()
{
$articles = Post::user()->all();
$number = 5;
User::find(1)->articles()->map(function(Article $article) {
$article['comments'] = $article->getCommnets($number);
return $article;
});
return Response::json($articles);
}
}
Is there a better way to do it? because I use this a lot and it does not seams right.
Judging from the Laravel source code, no – it's not possible to pass an extra argument to this magic accessor method.
The easiest solution is just to add another, extra method in your class that does accept any parameters you wish – and you can use that method instead of magic property.
Eg. simply rename your getCommentsAttribute() to getComments() and fire ->getComments() instead of ->comments in your view, and you are good to go.
I just set a public property on the model. At the accessing point, I update that property to my desired value. Then, in the attribute method, I read the desired arguments from that property. So, putting all of that together,
// Model.php
public $arg1= true;
public function getAmazingAttribute () {
if ($this->arg1 === false)
$this->relation()->where('col', 5);
else $this->relation()->where('col', 15);
}
// ModelController.php
$instance->arg1 = false;
$instance->append('amazing');
It is been a while for this question, but maybe someone will need it too.
Here is my way
{
/**
* #var string|null
*/
protected ?string $filter = null;
/**
* #return UserSettings[]|null
*/
public function getSettingsAttribute(): ?array
{
return services()->tenants()->settings($this)->getAll();
}
/**
* #return FeatureProperty[]|null
*/
public function getFeaturePropertiesAttribute(): ?array
{
return services()->tenants()->featureProperty($this)->getListByIds($this->filter);
}
/**
* #param string|null $filter
* #return Tenant
*/
public function filter(string $filter = null): Model
{
$this->filter = $filter;
return $this;
}
Accessor is using some service to get values. Service accepts parameters, in my case string, that will be compared with featureProperty->name
Magic happens when you return $this in filter method.
Regular way to call accessor would be:
$model->feature_properties
Extended way:
$model->filter('name')->feature_properties
Since filter argument can be null, we can have accessor like this:
$filter = null
$model->filter($filter)->feature_properties
In case you would like to play with it a little more you can think about overriding models getAttribute or magic __call methods implementing filter in manner which will be similar to laravel scopes
I know its an old question, but there is another option, but maybe not the best:
$articles = Post::user()->all();
$number = 5;
$articles->map(function($a) use($number){
$a->commentsLimit = $number;
return $a;
});
And then in getCommentsAttribute():
return $this->comments()->paginate($this->commentsLimit);

Laravel eloquent issue with constructor

I have a model which contains many methods.
class UserModel extends Eloquent{
private $active;
function __construct() {
$this->active = Config::get('app.ActiveFlag');
}
protected $table = 'User';
protected $fillable = array('usr_ID', 'username');
public function method1(){
//use $active here
}
public function method2(){
//use $active here
}
}
Controller:
$user = new UserModel($inputall);
$user->save();
Without constructor, it works fine. However, with constructor it doesn't save the user (the query which is generated doesn't have any fill attributes or values). The query is as follows:
insert into User() values();
Any inputs please?
Well yes, that's because you override the Eloquent constructor which is responsible to fill the model with values when an array is passed. You have to pass them along to the parent with parent::__construct():
public function __construct(array $attributes = array()){
parent::__construct($attributes);
$this->active = Config::get('app.ActiveFlag');
}
Your model's constructor doesn't accept any parameters - empty (), and you are creating new instance of UserModel in your controller adding $inputall as a parameter.
Try to refactor your contructor according to this:
class UserModel extends Eloquent {
public function __construct($attributes = array()) {
parent::__construct($attributes);
// Your additional code here
}
}
(Answer based on other Eloquent contructor question)

Laravel : add new row in model table

I have a user model defined, and I'm trying to add a new user in my database using a form, what is the best and fastest way to do it, I have read something about model forms binding but I think it's just for updating not for adding new rows.
I'm new in Laravel and couldn't find some good tutorials, I must recognize that Laravel documentation is really poor, so any links to some good and well explained tutorials are welcomed.
Thank you
Assumed that, you have a User model (app/models/User.php) the one came with Laravel by default, which may look like this:
use Illuminate\Auth\UserInterface;
use Illuminate\Auth\Reminders\RemindableInterface;
class User extends Eloquent implements UserInterface, RemindableInterface {
protected $table = 'users';
protected $hidden = array('password');
public function getAuthIdentifier()
{
return $this->getKey();
}
public function getAuthPassword()
{
return $this->password;
}
public function getReminderEmail()
{
return $this->email;
}
}
Now, from a controller (Basically) you may use somethng like this:
$user = new user;
$user->username= 'Me';
$user->email = 'me#yahoo.com';
// add more fields (all fields that users table contains without id)
$user->save();
There are other ways, for example:
$userData = array('username' => 'Me', 'email' => 'me#yahoo.com');
User::create($userData);
Or this:
User::create(Input::except('_token'));
This requires you to use a property in your User model like this:
class User extends Eloquent implements UserInterface, RemindableInterface {
protected $fillable = array('username', 'email');
// Or this, (Read the documentation first before you use it/mass assignment)
protected $guarded = array();
}
Since, you are still new to Laravel you may use first example and read about Mass Assignment, then you may use the second one if you want.
Update:
In your controller, you may use Input::get('formfieldname') to get the submitted data, for example:
$username = Input::get($username);
So, you can use these data like this:
$user = new User;
$user->username= $username;
Or directly you can use:
$user->email = Input::get($email);
$user->save();
In the form, you have to set the form action, where you'll submit the form and in this case you have to declare a route, for example:
Route::post('user/add', array('as' => 'user.add', 'uses' => 'UserController#addUser'));
Then in your controller you have to create the method addUser, like this:
class UserController extends addUser {
// other methods
public function addUser()
{
$user = new user;
$user->username = Input::get('username');
$user->email = Input::get($email);
$user->save();
}
}
In your form you may use this:
Form::open(array('route' => 'user.add'))
Read the documentation properly, you can do it easily.

Categories