In Laravel 4, I have a model linking to a database table. Let's call it Model.
Say that this model has a database column called Property A and Property B.
When I make get request call to my model, i.e. Model::all() or Model::find($id), I don't want to return Property A or Property B, but some kind of function of the two, which appears to the front-end as some kind of read-only field, i.e. Property C.
Do I need to use a presenter library here, or is there a way by overriding model functions within Laravel 4?
The key for me here is that the property shows up when I call Model::all()
EDIT:
From my understanding this should return an attribute with the name foo constantly with the value "foo":
Model
class DiscountLink extends Eloquent {
protected $table = 'discountLinks';
protected $hidden = array('tag');
protected $fillable = array('name', 'currency', 'language', 'price', 'instalments', 'expires', 'active', 'foo');
public function getFooAttribute()
{
return "foo";
}
}
Controller
class DiscountLinkController extends \BaseController {
public function index()
{
return DiscountLink::all();
}
}
Use an accessor in your Model. To concatenate A and B, for instance:
public function getPropertyCAttribute()
{
return $this->attributes['property_a'] . ' ' . $this->attributes['property_b'];
}
And then you can access Model::find($id)->propertyC.
If you want the attribute to be automatically included in your model's results array (e.g. if you're sending the results of Model::all() or Model::get() as JSON, for example), add an $appends declaration to the top of your model:
protected $appends = array('PropertyC');
If the function is something that can be done in the database (like concatenation, sum, etc.), you could also add a DB::raw command to your query, like:
Model::select(*, DB::raw('CONCAT(PropertyA, " ", PropertyA) AS PropertyC'))->...
Related
Environment
Laravel 9
php 8.0
I have this mutator function to transform a value from 4 decimal places to 2 decimal places. I want to test out but the Attribute::make function not returning value, below is code for the model
class SubjectWithFee extends Model
{
protected $table = 'sjfee';
protected $primaryKey = 'IndexID';
protected $fillable = [
'Amount',
'CurrencyID',
'ProgramID',
];
public $timestamps = false;
protected function Amount(): Attribute
{
return Attribute::make(
get: fn ($value) => sprintf('%0.2f', $value),
);
}
Although when I did the test it access the attribute correctly when putting dd('test') before the return but on the get function cannot be access
Anyone knows the problem?
Update
The column "Amount" starts with capital letter
Initially the source code was from laravel version 5 and someone upgraded to 9.
Update 2
All my setters are working, but only getters are not.
Try this different approach, instead of the current function you have:
public function getAmountAttribute($value)
{
return sprintf('%0.2f', $value);
}
Add the attribute name to the appends property of your model.
Appending Values To JSON
Occasionally, when converting models to arrays or JSON, you may wish
to add attributes that do not have a corresponding column in your
database. To do so, first define an
accessor for the
value:
After creating the accessor, add the attribute name to the appends
property of your model. Note that attribute names are typically
referenced using their "snake case" serialized representation, even
though the accessor's PHP method is defined using "camel case":
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* The accessors to append to the model's array form.
*
* #var array
*/
protected $appends = ['amount'];
}
I created an Eloquent Model :
class VehicleDetails extends Model
{
//
protected $table = 'v_vehicle_details';
protected $primaryKey = 'model_id';
public function tariffs()
{
return $this->hasMany('App\Tariffs', 'vehicle_model_id', 'model_id');
}
}
The table structure for the same is v_vehicle_details is
v_vehicle_details
The table structure for tariffs is
t_tariffs
The Model is being called in controller like :
public function booking_view(){
$vehicle_details = new VehicleDetails();
return $vehicle_details->find(5)->tariffs();
}
What I need is to get vehicle details with all tariffs, But when I try that it throws an error Illuminate\Database\Eloquent\Relations\HasMany could not be converted to string. Can somebody please help, I am new to laravel.
These are not actual tables, But views.
change to
return VehicleDetails::with('tariffs')->find(5);
This is because calling the method ->tarrifs() will return a relationship object in this case HasMany this means you still have to perform the query on this object to get the results.
If you use it as a property ->tarifs without () it will perform the query.
This is the same as tarifs()->get() just a shortcut.
Change your function to:
public function bookingView(){
$vehicle = VehicleDetails::with('tariffs')->find(5);
return view('your.view', compact('vehicle'));
}
The with() will eager load the tariffs relation.
You can acess your tariffs in your view like this:
{{$vehicle->tariffs->anyAttributeYouWantToAccess}}
I had an accessor set on my Eloquent model that worked fine, but the associated database query was getting run once for every instance of the model I created. On my index page this meant 5 dozen queries.
<?php
class Thingy extends Model {
protected $appends = ["parentType"];
public function getParentTypeAttribute($value) {
return self::where("type"=>$this->type, "parent"=>1)->value("name");
}
}
class ThingyController extends Controller {
public function index() {
$thingys = Thingy::all();
return view("things.index", compact("thingys"));
}
}
To explain briefly: there are two classes of "thingy" in the same database table, the class being indicated by a boolean value named "parent." I want to get the name of the parent when I access the child. I know this should be two tables but it's not.
I wanted to reduce the number of database reads, so I tried changing it to a relationship instead. I figured this way I could take advantage of eager loading.
<?php
class Thingy extends Model {
public function parent() {
return $this->hasOne("Thingy", "id")->where("type"=>$this->type, "parent"=>1);
}
}
class ThingyController extends Controller {
public function index() {
$thingys = Thingy::with(["parent"]);
return view("things.index", compact("thingys"));
}
}
The problem is that within the relationship method, $this is an empty instance of the model, unlike in the accessor, so $this->type is null.
Is there a way to access properties of the model I'm working with from within a relationship method?
Figured that out. Since I'm essentially doing a self-join on the same table, I can specify the "local" and "foreign" ID columns as the column I'm trying to match:
public function parent() {
return $this->hasOne("Thingy", "type", "type")->where("parent"=>1);
}
I guess the key concept was to remember that I'm defining a relationship between two instances of the model, which is independent of the particular instances I'm dealing with.
I have a table called payments which contains a field called Vendor ZIP.
I have a table called 201502_postcodes and my "join" in this case is the postcode field in this table.
How do I return field values in this 201502_postcodes table using Eloquent?
My Models are;
<?php namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Payment extends Model {
public function postcodeExtract()
{
return $this->belongsTo('App\Models\PostcodeExtract', 'postcode', 'Vendor ZIP');
}
_
<?php namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class PostcodeExtract extends Model {
protected $connection = 'postcodes';
public function scopeFromTable($query, $tableName)
{
return $query->from($tableName);
}
public function payment()
{
return $this->hasMany('App\Models\Payment', 'Vendor ZIP', 'postcode');
}
So, I have a scope on this model because the 201502 part of my table name is a variable (in that, a new one comes in every quarter).
In my controller... I have no idea what to put. I don't know how to get both scope and relationship to work. How can I write a query that will take a postcode/zip and output one of the fields from the (do I refer to them as "methods"?) postcode extract table?
It is not a duplicate of this question Laravel 4: Dynamic table names using setTable() because relationships are not involved or discussed on that question.
--- UPDATE ---
If I am to use getTable - would it go something like this...
class PostcodeExtract {
public function setTableByDate($selected_tablename)
{
$this->table = $selected_tablename;
// Return $this for method chaining
return $this;
}
public function getTable()
{
if (isset($this->table))
$this->setTableByDate($this->table);
return $this->table;
}
}
And then I would use it in my controller like;
$selected_tablename = 201502_postcode //created by some other controller
$postcode_extract = new PostcodeExtract;
$data = $postcode_extract->setTableByDate($selected_tablename)->get()->toArray();
The Carbon stuff isn't really relevant. I have a lookup to get those tablenames the fact the prefix with a date like value shouldn't mean it's treated like a date.
There are a couple of things going on here.
scopeFromTable() is redundant
Laravel employs magic methods to handle calls to undefined methods. Calling from() on the model will actually call from() on the models internal Query object (assuming you didn't define a method called 'from' on the model itself). It's worth reading the __call and __callStatic methods on the Model class.
relationships use getTable()
Another aspect of the Laravel is the concept of convention over configuration. This basically means that the framework assumes some things so that you don't have to define every detail. In regards to table naming convention, it will naturally use a table name derived from the class name.
// Uses table 'foos'
class Foo {}
There are a few ways to change this behavior. First, you can define a 'table' data member like this.
class Foo {
protected $table = 'bars';
}
If you need a more dynamic behavior, then you can redefine the getTable method.
class Foo {
public function getTable()
{
// return your special table name based on today's date
}
}
Ultimately the models and their relationships refer to getTable to figure out what the table names should be.
your use cases
If you only ever need to query the current table, then I would suggest redefining getTable.
If you need to query both current and past tables, then I suggest pairing a new method along side redefining getTable
class Foo {
public function setTableByDate(\DateTime $date)
{
$this->table = // generate table name from $date
// Return $this for method chaining
return $this;
}
public function getTable()
{
if (isset($this->table))
$this->setTableByDate(\Carbon\Carbon::now());
return $this->table;
}
}
With this in place, you don't have to worry about the table name in your controller or anywhere else unless you need to query past records.
setting the table by date per user
$foos = Foo::setTableByDate($user->some_date)->where(...)->get();
I'm on laravel 4.2, trying to get an objects associative relation yet for some reason I am getting null.
Models:
class Keg extends Eloquent {
protected $fillable = ['beer_distribution_id', 'status', 'keg_size'];
public function beer_distribution()
{
return $this->belongsTo('BeerDistribution');
}
}
class BeerDistribution extends Eloquent {
protected $fillable = ['beer_id', 'distributor_id'];
public function kegs()
{
return $this->hasMany('Keg');
}
}
My Query:
$distirbution = Keg::find($tap->keg_id)->beer_distribution
This query returns null: I know the keg object is found and I know that the object has the beer_distribution_id.
I tried specifying the foreign key in the model like so:
class Keg extends Eloquent {
protected $fillable = ['beer_distribution_id', 'status', 'keg_size'];
public function beer_distribution()
{
return $this->belongsTo('BeerDistribution', 'beer_distribution_id');
}
}
I thought that might be necessary since the Model is camel case.
I know this is super simple, and I am in fact querying many relations succesfully through out my application, but for some reason this one is not working. Any thoughts on what I might be missing?
The problem is the function name of the relationship. Laravel expects it to be in camel case: beerDistribution().
Change that and you should be good. (You don't have to specify the foreign key, Laravel will convert camelCase to snake_case)