I have a Laravel Eloquent model User, which has a table with username and email columns. I need to add a property for the model on runtime, something like $user->secure. This property doesn't need to go to database.
When i add this property and hit $user->save() i get an error saying i don't have database column 'secure'. I can unset 'secure' before save but somehow it feels there should be a better way to do so. Any suggestions?
Just add an attribute to your class.
class User extends Eloquent {
public $secure;
// ...
}
Note that it is better to declare it protected and add corresponding accessors and mutators (getSecure and setSecure methods) to your model.
For those who still find this post (like I did), you may need to explicitly declare the property as null to prevent it from being automatically added to the attributes array, and thus being added to INSERT/UPDATE queries:
class User extends Eloquent {
public $secure = null;
// ...
}
Source: http://laravel.io/forum/02-10-2014-setting-transient-properties-on-eloquent-models-without-saving-to-database
If you intend to make use of that add-on property, then $append will blow your mind.
http://laraveldaily.com/why-use-appends-with-accessors-in-eloquent/
Related
In laravel 5.4, I'm able to retrieve fillable fields by using fillable index of model instance.
$model = new AnyClass();
dd($model['fillable']);
The above code prints all fillable fields of AnyClass. But the same code prints null on laravel 5.6. I know I can retrieve fillable fields using $model->getFillable(). My question is what is the reason / why it is not working in laravel 5.6 but works in 5.4?
From the upgrade guide here I believe this is the answer to the question:
Model Methods & Attribute Names
To prevent accessing a model's private properties when using array access, it's no longer possible to have a model method with the same name as an attribute or property. Doing so will cause exceptions to be thrown when accessing the model's attributes via array access ($user['name']) or the data_get helper function.
If you look at Laravel's source code you'll see the difference.
The Model class, which is extended by the application models, implements the ArrayAccess interface, which, among others, force the class to define the offsetGet method.
In Laravel 5.4 the offsetGet method looks like:
public function offsetGet($offset)
{
return $this->$offset;
}
which means that if you call $model['fillable'], you actually call $model->offsetGet('fillable') which actually returns the fillable property of the class.
I couldn't find the Laravel 5.6 tag but I'm pretty sure it is the same code as Laravel 5.5.45. In this version the offsetGet method was changed to:
public function offsetGet($offset)
{
return $this->getAttribute($offset);
}
which means that it actually returns the attribute if found or null otherwise.
In Laravel 7, I'm doing this by calling the getFillable method on a new instance of my model. Like so:
$model = new MyModel();
$fillable = $model->getFillable();
Late to the party but I don't like the concept of having to always instance a Model, specially if you're using Eloquent serialization.
Let's say you wanted to build some filters, but wanted to whitelist the columns based on the model's fillable. You don't want to instance an entire model, so you can instead use reflection:
(new ReflectionClass(MyModel::class))->getDefaultProperties()['fillable']
See it working over at 3v4l.org - Here I demonstrate why you potentially wouldn't want to instance this model due to having serialization and always eager loading.
Change the property in the class to public $fillable = [ instead of protected $fillable = [
I have a view that I'm trying to re-use for two different actions to display data from the database. For one of those actions, an Eloquent collection object is passed to the view, and data is retrieved with
#foreach($buildings as $key=>$value)
{!! $value->build_name !!}
Obviously 'build_name' is a column in the table. So far simple..
Now I need this same view to display data that requires a lot of processing and it's not possible to generate an eloquent statement to pass to the view.
In order to re-use the $value->build_name code, I'm assuming I have to still pass an object (model??) to the view.
I have a Building.php Model
class Building extends Model
{
protected $fillable =[
'buildingtype_id',
'build_name',
];
and I'm thinking I could just add public $build_name; to the Building model, but then I should also add a method to set and get the $build_name. So my Building Model will now look like..
class Building extends Model
{
public $build_name;
protected $fillable =[
'buildingtype_id',
'build_name',
];
public function getBuildName () {
return $this->build_name;
}
public function setBuildName ($name) {
$this->build_name = $name;
}
And I can just create the object myself in the controller...
If I do this, is {!! $value->build_name !!} still appropiate for the view? Or should I now be using {!! $value->getBuildName() !!}
Or am I missing a key concept somewhere? I'm still new to Laravel and OOP.
Edit
I just implemented this, and it's not working. If I add the public $build_name attribute to the model, getBuildName does not return anything, however if I remove public $build_name it does... (which would break my attempting to create that object manually)
When you declare public $build_name, this will override (or more precisely, reset) any other field with the same name in the model. So, you'll have to call setBuildName() setter method before you get it.
I just implemented this, and it's not working. If I add the public $build_name attribute to the model, getBuildName does not return anything
That's because you've called the getter method before the setter, so there is nothing (null) set in the public variable $build_name.
Although you haven't quite mentioned why exactly you want to reuse the Eloquent model, but you can achieve your desired purpose with a little tweak on the model's setter methods:
class Building extends Model
{
/* ... */
public function setBuildName ($name) {
$this->build_name = $name;
return $this;
}
}
Notice returning the current object ($this) in case you would want to chain multiple setter methods in one go.
e.g.:
$model->setBuildName('some name')->setBuildHeight(400);
UPDATE
You can use the default eloquent model to serve your purpose (hence, ridding you of making a duplicate class to achieve roughly the same effect).
Now suppose you have your model Building and would like to set it's attributes manually, then, the following operation on the model is still appropriate:
$building = new App\Building();
$building->build_name = 'Some Building Name'; // you can substitute this with your setter method as well
$building->build_height = 110; // assuming you have a column named `build_height` in your model's table
Note that the only difference in what you'd be doing here is:
You DON'T declare public variables at all in the Eloquent model.
You don't call Eloquent's save() method on the model to persist the manually set data (giving it a transient behavior).
The model now is totally eligible to be passed to your view and you can access it's attributes as you would with a regular model:
<!-- in your view -->
{{ $building->build_name }}
{{ $building->build_height }}
As an alternative approach to setting your arbitrary data, you can have a single setter which accepts an array of key value data to be stored in the model (e.g. ['build_name' => 'Building Name', 'build_height' => 110]):
//in class Building
public function setData($data){
$this->build_name = $data['build_name'];
$this->build_height = $data['build_height'];
}
I am trying to create an observer for an Eloquent model to work as a logger for the changes on that model. I am not sure what parameters are passed to the observer's methods from the model. If the parameters don't include an instance of the updated model, is there another way around it without having to redefine the model class and override the needed methods?
class UserObserver{
public static function saved($user){
Activity::create([
"activity-name" => "save",
"user" => $user->id
]);
}
}
I found out that the model is actually passed, my mistake was not adding user property to the fillable array in the Activity model.
usually, I get an exception when my application tries to update fields that are not included in the fillable array, but this time I didn't. anybody knows why?
I want to add some joins onto my Auth::user() query. How do I do this without creating a brand new query? I just want to be able to make the default call of Auth::user() different than:
SELECT * FROM `users` WHERE `id` = ?
to
SELECT * FROM users INNER JOIN user_icons ON user_icons.ID = users.iconid WHERE `id` = ?
I'm using the default model User class.
Laravel provides a way for you to extend the Auth functionality. First, you need to create a class that implements the Illuminate\Auth\UserProviderInterface. Once you have your class, you call Auth::extend() to configure Auth with your new class.
For your case, the easiest thing for you to do would be to create a class that extends Illuminate\Auth\EloquentUserProvider. You'll want to update the retrieveBy* methods to add in your custom joins. For example:
class MyEloquentUserProvider extends Illuminate\Auth\EloquentUserProvider {
public function retrieveById($identifier) {
return $this->createModel()->newQuery()->join(/*join params here*/)->find($identifier);
}
public function retrieveByToken($identifier, $token) {
// your code with join added here
}
public function retrieveByCredentials(array $credentials)
// your code with join added here
}
}
Once your class is fleshed out, you need to tell Auth to use it:
Auth::extend('eloquent', function($app) {
return new MyEloquentUserProvider($app['hash'], $app['config']['auth.model']);
});
The first parameter to the Auth::extend method is the name of the auth driver being used as defined in app/config/auth.php. If you want, you can create a new driver (e.g. 'myeloquent'), but you'd need to update your Auth::extend statement and your app/config/auth.php driver.
Once all this is done, Auth::user() will end up calling your MyEloquentUserProvider::retrieveById method.
Fair warning: I have not actually done this myself, and none of this is personally tested. You will probably want to check out the documentation (L4.1 docs, L4.2 docs) and look at the Laravel code.
Other notes:
People have already chimed in that this is probably not what you want to do. However, the this information may be helpful to you and others looking to extend Auth for some other reason.
Considering your inner join, if a user does not have an associated user_icons record, Auth::user() will not return a record anymore, and the user probably won't be able to log in at all.
If you have 1:n relation:
Add a "icons" table to you database with a foreign key "user_id".
Add a "Icon" Model to your models.
<?php
class Icon extends Eloquent{
...
}
?>
In Model Class "User" add a function:
public function icons() {
return $this->hasMany('Icon');
}
Now you can do this:
$userIcons = Auth::user()->icons();
I am currently working on a web app that has been set up using the Repository/Service Layer Design Pattern, i.e. I have service layer that does any necessary business logic before running any methods within the repository. I have facades for each one of my models which access their respective service layers, and this has been fine for the most part. However, now that I am trying to set up Eloquent relationships, the facades seem to be causing a massive headache as I am not sure which direction I should be going.
Take the following code:
class Account extends Eloquent {
// Our table name
protected $table = "accounts";
// Our primary key
protected $primaryKey = "id";
/**
* Role Relationship
*
* Returns a list of roles associated with
* this account
*/
public function roles() {
return $this->hasMany('Role');
}
}
This will not work as is, because instead of using the entity class of Role, it is using the Role Facade. I have figured out a workaround for this, by setting an alias for the Entity with a slightly different name, such as RoleEntity so that
public function roles() {
return $this->hasMany('RoleEntity');
}
will work, however this doesn't seem like the most optimal solution.
My question is, is the practice ok? Or better yet, should this be happening at all? And if not, how do I fix it/where did I go wrong?
You have two classes with the same name in the same namespace. Use different namespaces so you can use the same class names.
I usually use \Models to locate my models classes.
At the top of each model file:
namespace Models;
In your controller or any part of your app:
\Models\Role::first();
Note that changing the namespace on your model will require you to add the namespaces of other classes i.e. Str, Eloquent, Url, Redirect, etc.
use Eloquent;
use URL;
In your model, you also have to pass the namespaces in the relationship functions, i.e.:
public function roles() {
return $this->hasMany('\Models\Role');
}