here is my Model:
class MyTable extends CActiveRecord{
...
public function myOwnFunction{
...
$this->find($myCriteria, $myParams);
$myLocalVar = $this->MyTableColumn;
...
}
}
My objective is to retrieve the record inside a function of MyTable CActiveRecord class
If I echo myLocalVar I find it empty
I guess I'm making a philosophical error... What is it? And what's the correct way to reach my objecive, if there is one?
Related
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.
It is possible to extend or use different class during run time?
Example:
Let say we have a model called Player (Our A -model)
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Player extends Model{
}
And we have 2 other models (B and C models)
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
protected $connection= 'db_b';
class PlayerInfoB extends Model{
function getName(){
return $this->name;
}
}
Our C model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
protected $connection= 'db_c';
class PlayerInfoC extends Model{
function getName(){
return $this->name_g;
}
}
How Model A (Player) can extend Model B or C during run time based on configuration or other data
Why I need this.
I have a 2 or more different tables, this tables columns have different names, so for example:
Table 1 - name
Table 2 - name_g
Table 3 - name_full
So I need a wrapper that I can always call getName(), without checking what table is used now.
$player = Player::get();
echo $player->getName();
If something is not clear, please comment and I will update my question.
Update based on madalin-ivascu answer can be done this way?
class Player extends Model{
protected $model;
public function __construct(){
$this->setModel();
parent::__construct();
}
protected function setModel(){
$this->model = $this->column_model_name
}
function getAttributeName(){
return $this->model->getName();
}
}
It is not possible to compose a calss at runtime without using eval or or dirty hacks. You have to reconsider your class design, because it's very unlikely that you need to do that with a good design.
What you can do is changing table and db connection at runtime on the model instance using methods setTable and on:
Player::on('db_b')->setTable('player_info_b')->find($id);
Another approach (preferable) would be defining the model PlayerInfoC and PlayerInfoB that extend your Player model, and then based on your condition you instantiate the class B or C when needed.
In your code your script must have a state that you check in order to know when to use the correct model?
In this case why not use a parameter in get name?
class Player extends Model{
function getName($field){
return isset($this->{$field}) ? $this->{$field} : null;
}
}
If you do this a lot then use magic methods:
class Player extends Model{
function __get($key){
return isset($this->{$field}) ? $this->{$field} : null;
}
}
...
echo $myModelInstance->{$field};
http://php.net/manual/en/language.oop5.overloading.php#object.get
In Laravel when you have pulled data back via a collection method it does this magic method anyway since all attributes are stored in a nested object called attributes so the __set() and __get() looks something like this:
function __get($key){
return isset($this->attributes->{$key}) ? $this->attributes->{$key} : null;
}
function __set($key, $value){
return $this->attributes->{$key} = $value;
}
The latter with attributes sub set is advised, this way you prevent data conflicts with database column names returned vs names you have already used in a model.
This way you only have to manage one attribute name as a reserved name in every model you create and not worry amount the hundreds of var names you use overwriting another in a model or extension of a model.
use that model value to call the function
$player = Player::get();
echo Player::getName($player->id,'PlayerInfoC');
in the Player model you simply call
public static function getName($id,$class)
return $class::where('player_id',$id)->getName();//each class will have the function
}
ps: you will need to do some validation to test if the class exist with that name
another option will be to create relationships between the models
I have a polymorphic relationship with Laravel.
My polymorphic relationship is Message->messageable becoming either Group or Chat.
Going from Message->messageable will give me the proper result(e.g the group or chat the message is associated with).
Going from example Group::first()->with('messages')->get() will return an empty messages array.
My db tables for message are
messageable_id
messageable_type
My model methods are as follows
class Message extends Model
{
public function messageable(){
return $this->morphTo();
}
^works as intended
class Group extends Model
{
function messages(){
return $this->morphMany('Message','messageable');
}
class Chat extends Model
{
public function messages(){
return $this->morphMany('Message','messageable');
These return empty.
example data used:
1
Any ideas?
You relationships are incorrect. You need to reference the full class namespace, not just the class name itself. E.g.
class Group extends Model
{
public function messages()
{
return $this->morphMany(\App\Models\Message::class', 'messageable');
}
}
Forgot to include namespaces in my Database messageable_type.
Changed from Group to App\Group.
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 have two classes. A and B. The class A extends Laravel, the class B extends A.
They rappresent two tables. The table associated with the class B doesn't have a primary key, also a foreign key (A_id). The situation is this:
class A extends Eloquent
{
protected $table = 'a';
}
class B extends A
{
protected $table = 'b';
protected $primaryKey = 'a_id';
}
I need to specify the primary key in class B cause laravel tries to build the B object with
SELECT * FROM B where id = ?
but the field id doesn't exists.
The problem is when i try to access to an A method from B object es
$b = B::find(1);
$b->method_in_a_class();
the method called execute a query in a C table (also another class), linked with A table, not with B table, so the framework do this query:
SELECT * FROM C WHERE B_id = ?
but it would be
SELECT * FROM C WHERE A_id = ?
Why??
Thank you
probably the easiest way to solve the problem is to use the one-to-one relationship, so that you have:
class A extends Eloquent
{
protected $table = 'a';
public function b()
{
return $this->hasOne('B');
}
}
class B extends Eloquent
{
protected $table = 'b';
public function a()
{
return $this->belongsTo('A');
}
}
Hope this could solve your problem!
If you do not have a primary key in the second table, you should consider merging the tables. Without the primary id, you are not able to use it as a Eloquent model anyway. You have this problems probably because of a bad database design.
First off, to answer your question: Eloquent uses B name for foreign key, because you didn't tell it not to.
Imagine this scenario (not entirely like yours, but it's a real one): Post model, Category model, RootCategory model:
class Category extends \Eloquent {
public function posts()
{
return $this->hasMany('Post');
}
}
// categories are hierarchical, self referencing table
class RootCategory extends Category {
// global scope and stuff
protected $table = 'categories';
}
class Post extends \Eloquent {
public function category()
{
return $this->belongsTo('Category');
}
}
Now, with above setup, calling simple dynamic property will throw - column not found 'posts.root_category_id' which is probably what happened in your case.
The reasons for this are:
You didn't specify the foreign key to look for...
...so Eloquent calls generic getForeignKey() method to guess it
getForeignKey() uses $this, which is RootCategory object, so the foreign key will be root_category_id.
So there are 2 ways of solving this issue:
1 better
// RootCategory model
public function getForeignKey()
{
return 'category_id';
}
2 also working, however requires editing parent class.
// Category model
public function posts()
{
return $this->hasMany('Post', 'category_id');
}