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

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.

Related

Eloquent model add extra column by default

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

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 4 - Using Eloquent Models in a custom library class

I made a library class that I am using for some common functions not provided by Laravel. It's been loaded into /config/app.php under the 'aliases' array, so that shouldn't be the problem.
When I call a method from my class ("InfoParse"), my conroller returns a blank page. I think this has to do with the fact that I'm calling a method from the library which uses Eloquent to interface with the database. I tried adding
use Illuminate\Database\Eloquent\Model;
to the top of the file, but that didn't help either.
Is there a specific way I should be setting up my class file so I can use either the DB:: class or Eloquent class?
Below is the function in question:
/**
* Check to see if this student is already recorded in our student table.
* If not, add the entry, then return true.
* #param int $cwid
* #return boolean
*/
public static function checkStudentTableRecords($cwid)
{
if(Student::where('cwid', '=', $cwid)->count() != 0)
{
return TRUE;
}
else
{ ##insert the student into our student table
$studentInfo = self::queryInfoFromCWID($cwid);
$studentEntry = new Student;
$studentEntry->cwid = $cwid;
$studentEntry->fName = $studentInfo['fName'];
$studentEntry->lName = $studentInfo['lName'];
$studentEntry->email = $studentInfo['email'];
$studentEntry->save();
return TRUE;
}
}
(note: the self::queryInfoFromCWID() function is calling a function defined earlier in the class)
After some investigation, it turns out I need to format my Eloquent Model calls like this:
if(\Student::where('cwid', '=', $cwid)->count() != 0)
...
$studentEntry = new \Student;
The backslash is necessary to avoid namespace collision within the Laravel4 application.

Yii - Error setting Active Record dynamic table names

Hi I'm trying to use a model that will generate dynamic table name from another database. I've managed to set the table name by overriding the tableName() function. But i'm getting an error saying
The table "powerDovakin_{FUS.THUM}" for active record class "PowersTransactions" cannot be found in the database
Here is the model class in question
<?php
class PowersTransactions
extends CActiveRecord {
public $symbol ;
public function __construct ($symbol) {
$this->symbol = $symbol;
}
/**
* #return string the associated database table name
*/
public function tableName () {
return "powerDovakin_{" . $this->symbol ."}";
}
/**
* #return array relational rules.
*/
public function relations () {
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array (
) ;
}
/**
* Returns the static model of the specified AR class.
* Please note that you should have this exact method in all your CActiveRecord descendants!
* #param string $className active record class name.
* #return InsidersTransactions the static model class
*/
public static function model ( $className = __CLASS__ ) {
return parent::model ( $className ) ;
}
/**
* Overriding parent getDbConnection to allow for use of different database
*/
public function getDbConnection () {
return Yii::app ()->powersDovakin ;
}
}
Now i've turned on logging and the trace shows that the error is being thrown when this query is being executed.. Here are some of the relevant lines from the stack trace
12:19:45.053172 trace system.db.CDbConnection
[ocak07jk4q3v8nfd535io8fdd4] Opening DB connection
in /var/www/html/PowerAnalysis/protected/models/PowersTransactions.php
(283)
in /var/www/html/PowerAnalysis/protected/models/PowersTransactions.php
(191)
in /var/www/html/PowerAnalysis/protected/views/realTime/_powerView.php
(9)
12:19:45.053564 trace system.db.CDbCommand
[ocak07jk4q3v8nfd535io8fdd4] Querying SQL: SHOW FULL COLUMNS FROM
`powerDovakin_{FUS.THUM}`
in /var/www/html/PowerAnalysis/protected/models/PowersTransactions.php
(191)
in /var/www/html/PowerAnalysis/protected/views/realTime/_powerView.php
(9)
in /var/www/html/PowerAnalysis/protected/views/realTime/view.php (715)
12:19:45.053858 error system.db.CDbCommand
[ocak07jk4q3v8nfd535io8fdd4] CDbCommand::fetchAll() failed:
SQLSTATE[42000]: Syntax error or access violation: 1142 SELECT command
denied to user 'user1'#'localhost' for table 'THUM}'. The SQL statement
executed was: SHOW FULL COLUMNS FROM `powerDovakin_{FUS`.`THUM}`.
in /var/www/html/PowerAnalysis/protected/models/PowersTransactions.php
(191)
in /var/www/html/PowerAnalysis/protected/views/realTime/_powerView.php
(9)
in /var/www/html/PowerAnalysis/protected/views/realTime/view.php (715)
From the above trace what i could find out is that Yii is putting backticks (`) around the dots and maybe interpreting the portion after the dots as a column name.
My question is how can i make Yii use this sort of table names. I wish i could change the table names but my hands are tied at this moment. I just can't change them as they are not mine. So again the table names are like
powerDovakin_{FUS.THUM} , powerDovakin_{ROH.THUM}, etc
Is it possible to make the model accept such names. Please provide any sort of help as i can't find any solution to this problem. I would really appreciate any help i can get on this.
Thanks, In Advance,
Maxx
the above code might give you the ability to fetch records from the tables but i don't think that you can insert any rows.
You need to call the constructor of the parent class in order to get the required functionality.
class PowersTransactions extends CActiveRecord {
public $symbol;
public function __construct ($symbol) {
$this->symbol = $symbol;
parent::__construct();
}
/**
* other code goes here
*/
}
Code above was tested and working

Activerecord-association: create new object (find class)

I have an model with a relation, and I want to instantiate a new object of the relations type.
Example: A person has a company, and I have a person-object: now I
want to create a company-object.
The class of the companyobject is defined in the relation, so I don't think I should need to 'know' that class, but I should be able to ask the person-object to provide me with a new instance of type company? But I don't know how.
This is -I think- the same question as New model object through an association , but I'm using PHPActiveRecord, and not the ruby one.
Reason behind this: I have an abstract superclass person, and two children have their own relation with a type of company object. I need to be able to instantiate the correct class in the abstract person.
A workaround is to get it directly from the static $has_one array:
$class = $this::$has_one[1]['class_name'];
$company = new $class;
the hardcoded number can of course be eliminated by searching for the association-name in the array, but that's still quite ugly.
If there is anyone who knows how this is implemented in Ruby, and how the phpactiverecord implementation differs, I might get some Ideas from there?
Some testing has revealed that although the "search my classname in an array" looks kinda weird, it does not have any impact on performance, and in use it is functional enough.
You can also use build_association() in the relationship classes.
Simplest way to use it is through the Model's __call, i.e. if your relation is something like $person->company, then you could instantiate the company with $company = $person->build_company()
Note that this will NOT also make the "connection" between your objects ($person->company will not be set).
Alternatively, instead of build_company(), you can use create_company(), which will save a new record and link it to $person
In PHPActiveRecord, you have access to the relations array. The relation should have a name an you NEED TO KNOW THE NAME OF THE RELATIONSHIP/ASSOCIATION YOU WANT. It doesn't need to be the classname, but the classname of the Model you're relating to should be explicitly indicated in the relation. Just a basic example without error checking or gritty relationship db details like linking table or foreign key column name:
class Person extends ActiveRecord\Model {
static $belongs_to = array(
array('company',
'class_name' => 'SomeCompanyClass')
);
//general function get a classname from a relationship
public static function getClassNameFromRelationship($relationshipName)
foreach(self::$belongs_to as $relationship){
//the first element in all relationships is it's name
if($relationship[0] == $relationshipName){
$className = null;
if(isset($relationship['class_name'])){
$className = $relationship['class_name'];
}else{
// if no classname specified explicitly,
// assume the clasename is the relationship name
// with first letter capitalized
$className = ucfirst($relationship);
}
return $className
}
}
return null;
}
}
To with this function, if you have a person object and want an object defined by the 'company' relationship use:
$className = $person::getClassNameFromRelationship('company');
$company = new $className();
I'm currently using below solution. It's an actual solution, instead
of the $has_one[1] hack I mentioned in the question. If there is a
method in phpactiverecord I'm going to feel very silly exposing
msyelf. But please, prove me silly so I don't need to use this
solution :D
I am silly. Below functionality is implemented by the create_associationname call, as answered by #Bogdan_D
Two functions are added. You should probably add them in the \ActiveRecord\Model class. In my case there is a class between our classes and that model that contains extra functionality like this, so I put it there.
These are the 2 functions:
public function findClassByAssociation($associationName)
Called with the name of the association you are looking for.
Checks three static vars (has_many,belongs_to and has_one) for the association
calls findClassFromArray if an association is found.
from the person/company example: $person->findClassByAssociation('company');
private function findClassFromArray($associationName,$associationArray)
Just a worker-function that tries to match the name.
Source:
/**
* Find the classname of an explicitly defined
* association (has_one, has_many, belongs_to).
* Unsure if this works for standard associations
* without specific mention of the class_name, but I suppose it doesn't!
* #todo Check if works without an explicitly set 'class_name', if not: is this even possible (namespacing?)
* #todo Support for 'through' associations.
* #param String $associationName the association you want to find the class for
* #return mixed String|false if an association is found, return the class name (with namespace!), else return false
* #see findClassFromArray
*/
public function findClassByAssociation($associationName){
//$class = $this::$has_one[1]['class_name'];
$that = get_called_class();
if(isset($that::$has_many)){
$cl = $this->findClassFromArray($associationName,$that::$has_many);
if($cl){return $cl;}
}
if(isset($that::$belongs_to)){
$cl = $this->findClassFromArray($associationName,$that::$belongs_to);
if($cl){return $cl;}
}
if(isset($that::$has_one)){
$cl = $this->findClassFromArray($associationName,$that::$has_one);
if($cl){return $cl;}
}
return false;
}
/**
* Find a class in a php-activerecord "association-array". It probably should have a specifically defined class name!
* #todo check if works without explicitly set 'class_name', and if not find it like standard
* #param String $associationName
* #param Array[] $associationArray phpactiverecord array with associations (like has_many)
* #return mixed String|false if an association is found, return the class name, else return false
* #see findClassFromArray
*/
private function findClassFromArray($associationName,$associationArray){
if(is_array($associationArray)){
foreach($associationArray as $association){
if($association['0'] === $associationName){
return $association['class_name'];
}
}
}
return false;
}

Categories