Eloquent model add extra column by default - php

I have a model called Book and I want to add an extra column to the default SQL.
At the moment the default sql looks like this:
SELECT * FROM `books`
But I want the default SQL to look like this:
SELECT *, "Hello" as `greeting` FROM `books`
So that I can do the following:
// in a controller function...
$book = Book::find(1);
echo $book->greeting; // Hello
$books = Book::all();
foreach($books as $book){
echo $book->greeting; // Hello
}
Is there any way I can achieve this?
Many thanks

Even though I wonder what the reason behind this is, you could override newQuery in your model
public function newQuery(){
$query = parent::newQuery();
return $query->selectRaw('*, "Hello" AS greeting');
}
Another way would be to use a scope:
public function scopeWithGreeting($query){
return $query->selectRaw('*, "Hello" AS greeting');
}
Usage:
$book = Book::withGreeting()->find(1);
If you really want the scope every time, you can use a global scope so you don't have to call withGreeting all the time.

Use an accessor. This one returns 'Hello' for $book->greeting if the model doesn't have a greeting set:
public function getGreetingAttribute($value) {
if(empty($value)) { return 'Hello'; } else { return $value; }
}

I think that the ORM woud not be able to manage that well enough so you are going to get into trouble sooner than later, if you can not modify your book model but still wnat to accomplish that , i suggest you create a new model pointing to a view constructed with
SELECT *, 'Hello' as `greeting` FROM `books`

You can use $appends Here is the example
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Book extends Model
{
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name',
];
/**
* The accessors to append to the model's array form.
*
* #var array
*/
protected $appends = ['greeting'];
public function getGreetingAttribute()
{
return 'Hello';
}
}
You don't need to write any SQL for this see more https://github.com/laravel/framework/blob/5.5/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php#L59-L64

Related

How to write count(*) SQL in DQL, Doctrine ORM

How to get count of rows in database by id?
SELECT count(*) FROM members;
Without performance issues. What are ways to write this query using entityManager?
I am using php version 5.6 and symfony 3
You have to use your EntityRepository
Add a function in it and write something like this:
$queryBuilder = $this->_em->createQueryBuilder()
->select('COUNT(e)')
->from('AppBundle:Entity', 'e');
return $queryBuilder->getQuery()->getSingleScalarResult();
Edit: Just saw Gregoire's answer. That will work. However, if you already have the Entity which has the relation, and it's initialized, the below would allow you to get this info without an additional query to the DB.
You could use the association and get it from the Collection (see Working with Associations in the docs
class SomeEntity
{
/**
* #var Collection|Member[]
*/
protected $members;
// other properties
// PHP >= 7 -> public function countMembers(): int
public function countMembers()
{
return $this->getMembers()->count();
}
// PHP >= 7 -> public function getMembers(): Collection
public function getMembers()
{
return $this->members;
}
// other getters/setters
}

How do I implement Gravatar in Laravel?

What's the fastest way to implement Gravatar URLs in Laravel? I have a mandatory email address field, but I don't want to create a new column for Gravatars, and I'd prefer to use the native Auth::user() attributes.
Turns out, you can use a Laravel mutator to create attributes that don't exist in your model. Assuming you have a User model with a mandatory email column in the corresponding users table, just stick this in your User model:
public function getGravatarAttribute()
{
$hash = md5(strtolower(trim($this->attributes['email'])));
return "http://www.gravatar.com/avatar/$hash";
}
Now when you do this:
Auth::user()->gravatar
You'll get the gravatar.com URL you're expecting. Without creating a gravatar column, variable, method, or anything else.
Expanding on Wogan's answer a bit...
Another example using a Trait:
namespace App\Traits;
trait HasGravatar {
/**
* The attribute name containing the email address.
*
* #var string
*/
public $gravatarEmail = 'email';
/**
* Get the model's gravatar
*
* #return string
*/
public function getGravatarAttribute()
{
$hash = md5(strtolower(trim($this->attributes[$this->gravatarEmail])));
return "https://www.gravatar.com/avatar/$hash";
}
}
Now on a given model (i.e. User) where you want to support Gravatar, simply import the trait and use it:
use App\Traits\HasGravatar;
class User extends Model
{
use HasGravatar;
}
If the model doesn't have an email column/attribute, simply override the default by setting it in the constructor of your model like so:
public function __construct() {
// override the HasGravatar Trait's gravatarEmail property
$this->gravatarEmail = 'email_address';
}

Laravel-eloquent: Call to undefined method Illuminate\Database\Eloquent\Collection::where()

I have two models in many-to-one relationship:
class Meal extends \Eloquent {
/**
* public Integer $id; - primary key
* public String $name;
*/
protected $fillable = array('id','name');
public function mealProperties()
{
return $this->hasMany('MealProperty');
}
}
class MealProperty extends \Eloquent {
/**
* public Integer $id; - primary key
* public Integer $meal_id;
*/
protected $fillable = array('id','meal_id');
public function meal()
{
return $this->belongsTo('Meal', 'meal_id');
}
}
if I ask for first meal first mealProperty everything go fine:
$mealProp = Meal::first()->mealProperties->first();
but if I ask for mealProperty with specific id of first meal this way:
$mealProp = Meal::first()->mealProperties->where('id','=','1')->first();
I get this error:
Call to undefined method Illuminate\Database\Eloquent\Collection::where()
I google what I'm doing wrong two hours, but still nothing.
If I can't use where method, what is possible way to get specific mealProperty?
Thank you for help!
UPDATE for Laravel 5:
Since v5 release there is a method where on the Support\Collection object, so this question/answer becomes irrelevant. The method works exactly like filter, ie. returns filtered collection straight away:
$mealProp = Meal::first()->mealProperties->where('id','=','1'); // filtered collection
// that said, this piece of code is perfectly valid in L5:
$mealProp = Meal::first()->mealProperties->where('id','=','1')->first();
You must distinguish Laravel behaviour:
(dynamic property) Eloquent Collection or Model
$meal->mealProperties
Relation Object
$meal->mealProperties()
Now:
// mealProperties is Eloquent Collection and you call first on the Collection here
// so basically it does not affect db query
$mealProp = Meal::first()->mealProperties->first();
// here you try to add WHERE clause while the db query is already called
$mealProp = Meal::first()->mealProperties->where('id','=','1')->first();
// So this is what you want to do:
$mealProp = Meal::first()->mealProperties()->where('id','=','1')->first();
You may try this:
$mealProop1 = Meal::first()->mealProperties->find(1); // id = 1
Or something like this:
$mealProops = Meal::first()->mealProperties;
$mealProop5 = $mealProops->find(5); // id = 5
$mealProop7 = $mealProops->find(7); // id = 7
Instead of this:
$mealProp = Meal::first()->mealProperties->where('id','=','1')->first();
Also, following should work:
$mealProp = Meal::first()->mealProperties->first();

How to customize date mutators in Laravel?

I've created a few datetime fields in my database, and as is described in Laravel documentation, I can "customize which fields are automatically mutated". However there's no example showing how it can be done, nor is there any search result. What should I do to make certain fields auto mutate?
For example, I created a table called "people" in migration, one of the fields is defined as this:
class CreatePeopleTable extends Migration {
public function up(){
Schema::create("bookings",function($table){
...
$table->dateTime("birthday");
...
}
}
}
And I defined a model for "people" in models:
class People extends Eloquent{
//nothing here
}
If I refer to the birthday of a People instance, it'll be string, instead of DateTime
$one=People::find(1);
var_dump($one->birthday);
//String
The date mutator should be able to convert it directly to Carbon object, but the documentation doesn't say much about how it should be implemented.
In your People model just add this array:
protected $dates = array('birthday');
Laravel's Model.php internaly merges your fields with the default ones like this:
/**
* Get the attributes that should be converted to dates.
*
* #return array
*/
public function getDates()
{
$defaults = array(static::CREATED_AT, static::UPDATED_AT, static::DELETED_AT);
return array_merge($this->dates, $defaults);
}
According to this doc, you can use model member function getDates() to customize which fileds are automatically mutated, so the following example will return Carbon instance instead of String:
$one = People::find(1);
var_dump($one->created_at);//created_at is a field mutated by default
//Carbon, which is a subclass of Datetime
But it doesn't say clearly how to add your own fields. I found out that the getDates() method returns an array of strings:
$one = People::find(1);
echo $one->getDates();
//["created_at","modified_at"]
So what you can do is appending field names to the return value of this method:
class People extends Eloquent{
public function getDates(){
$res=parent::getDates();
array_push($res,"birthday");
return $res;
}
}
Now birthday field will be returned as a Carbon instance whenever you call it:
$one = People::find(1);
var_dump($one->birthday);
//Carbon
What do you mean by: automatically mutated?
If you mean mutated after being retrieved from DB use Accessors and Mutators (Laravel docs).
Add this to your model:
public function getDateAttribute( $date )
{
// modify $date as you want, example
// $date = new \Carbon\Carbon($date);
// $date->addDay()
// return (string)$date
}
As Sasa Tokic says, add protected $dates = array('birthday'); to your People model like so:
class People extends Eloquent{
protected $dates = array('birthday');
}
You can then use Carbon to do clever things to this value, like so:
$people->birthday->format('jS F Y')
PHP's date() function docs (http://uk3.php.net/manual/en/function.date.php) and Carbon's docs (https://github.com/briannesbitt/Carbon) will help here:

CakePHP: Managing table names in a controller with a single variable

In controller A, I load a model not associated with this controller. I'm interested in managing the model name of controller B with a single variable, so I don't have to manually change many lines if the table/model B's name changes.
For example below is the controller A's code:
public $modelBName = 'ModelB';
public function controller_a_function() {
$this->loadModel($this->modelBName); // I use the variable here for model B
$this->ModelB->model_b_function(); // COMMENT #1
}
Question:
For the line commented "COMMENT #1," how do I use the variable name instead of explicitly written out word 'ModelB'? This line appears multiple times throughout the code, and I would like to use the variable $modelBName if possible. ModelB will likely not change, but if it does for some reason, it would be nice to just change one variable instead of editting multiple lines.
The simple answer; use this:
$this->{$this->modelBName}->find('all');
Note the curly brackets {} around the property name. more information can be found in the manual;
http://php.net/manual/en/language.variables.variable.php
A cleaner approach may be a sort of 'factory' method;
/**
* Load and return a model
*
* #var string $modelName
*
* #return Model
* #throws MissingModelException if the model class cannot be found.
*/
protected function model($modelName)
{
if (!isset($this->{$modelName})) {
$this->loadModel($modelName);
}
return $this->{$modelName};
}
Which can be used like this;
$result = $this->model($this->modelBName)->find('all');
debug($result);
And, if you don't want to specify the model, but want it to return a '$this->modelBName' automatically;
/**
* Load and return the model as specified in the 'modelBName' property
*
* #return Model
* #throws MissingModelException if the model class cannot be found.
*/
protected function modelB()
{
if (!isset($this->{$this->modelBName})) {
$this->loadModel($this->modelBName);
}
return $this->{$this->modelBName};
}
Which can be used like this:
$result = $this->modelB()->find('all');
debug($result);
I think you are confused between model name and table name. You can set a model to use a different database table by using the $useTable property, for example:
class User extends AppModel {
public $useTable = 'users_table'; // Database table used
}
class Product extends AppModel {
public function foo() {
$this->loadModel('User');
$this->User->find('all');
}
}
You should never need to change the name of the model, and if the name of the database table changes you can simply update the $useTable property in the model.

Categories