Yii Models: Using dynamic table name in a Yii model - php

I have a Yii model that will be using (later) multiple databases and the table prefix will be based on a code.
For example:
AMI_tablename, BMI_ AMI_tablename etc
These all tables are same but in different databases.
I want to know how could I provide the dynamic table name to Yii model at run time?
I tried to using a setter function but the parent class CActiveRecord gives an error as it does not get the value from the child model class.
so here is my model code (only the part I have problem)
class RevShareModel extends CActiveRecord
{
public $prefix;
public static function model($className=__CLASS__)
{
return parent::model($className);
}
/**
* #return string the associated database table name
*/
public function tableName()
{
return $this->prefix . '_revshare_model';
}
now somewhere in my controller
$obj = RevShareModel::model();
$obj->prefix ="BMI";
$obj->tableName();
$obj->findByPk(1);
exit;
But what I get the error is:
CDbException
The table "_revshare_model" for active record class "RevShareModel" cannot be found in the database.
C:\wamp\www\framework\db\ar\CActiveRecord.php(2264)
seems like when tableName() method is called by CActiveRecord it does not get $prefix.

You've got such error because the table name is actually stored in model's metadata. You can see that by checking contents of $model->getMetaData() which returns CActiveRecordMetaData object. To refresh metadata you should call $model->refreshMetaData() after changing the 'prefix' attribute of your model, e.g.:
...
$obj->prefix ="BMI";
$obj->refreshMetadata();
...
This will definitely do the trick.

You have to override the CActiveRecord::tableName method (probably in an abstract base class for your models). Here's what it does by default:
public function tableName()
{
return get_class($this);
}
And here's what you could change it to:
abstract class MyActiveRecord extends CActiveRecord
{
public $prefix; // should probably be private, your call
public function tableName()
{
return $prefix.'_'.get_class($this);
}
}

This is how I've solved that problem.
private static $_tableName;
public function __construct($tableName)
{
if(strlen($tableName) == 0)
{
return false;
}
if(strlen($tableName)>0){
self::$_tableName = $tableName;
}
self::setIsNewRecord(true);
}
public static function model($tableName)
{
if(strlen($tableName) == 0)
{
return false;
}
$className=__CLASS__;
if(strlen($tableName)>0){
self::$_tableName = $tableName;
}
return parent::model($className);
}
public function tableName()
{
return '{{'.self::$_tableName.'}}';
}
public function setTableName($tableName)
{
self::$_tableName = $tableName;
}
...
When I use this model, I simply put the name of the table in brackets:
$model = new ClassName($tableName);

I have same problem.
I have a model, but I want the data to be saved in different table .So I change the table name before save the data.
public $tbl_name = 'tbl_user_1';//default table
public function tableName()
{
return $this->tbl_name;
}
public function saveDataReg(){
$mKey = $this->selfMHash($this->username);
$modKey = $mKey % 2;
$this->tbl_name = 'tbl_user_' . $modKey;
$this->refreshMetadata();//change the default Metadata
$this->save();
}
public function selfMHash($key){
$md5 = substr(md5($key), 0, 8);
$seed = 31;
$hash = 0;
for($i=0; $i<8; $i++){
$hash = $hash * $seed + ord($md5{$i});
$i++;
}
return $hash & 0x7FFFFFFF;
}
Now I find the Metadata not be changed when the function of refreshMetadata be used.
Last I change the original code
public function refreshMetaData()
{
$finder=self::model(get_class($this));
$finder->_md=new CActiveRecordMetaData($finder);
if($this!==$finder)
$this->_md=$finder->_md;
//return var_dump($this);
}
public function new_refreshMetaData()
{
$finder=self::model(get_class($this));
$finder->_md=new CActiveRecordMetaData($this);
if($this!==$finder)
$this->_md=$finder->_md;
//return var_dump($this);
}
I override the function of refreshMetaData and chage the param of CActiveRecordMetaData.
$this->new_refreshMetadata();//use new function
Then it worked.
I don't understand the reason of problem.

You can use with MyModel::model()->tableName(), but please don't forget write {{table}}.

Related

Create DB record return wrong data types Laravel 5.8

In my model I have functions for create and find records.
Model
use Illuminate\Database\Eloquent\Model;
class Charge extends Model
{
use ChargeMutator;
public function __construct(array $attributes = [])
{
parent::__construct($attributes);
$this->request = $attributes;
}
public function add() {
$result = self::create($this->request);
return $result;
}
public function find($id) {
$record = self::find($id);
return $record;
}
}
And I have a mutator to mutate the values.
<?php
trait ChargeMutator
{
public function getCostAttribute($value)
{
return $this->attributes['cost'] = // call function A;
}
public function getTotalCostAttribute($value)
{
dd(gettype($value));
return $this->attributes['total_cost'] = // call function A;
}
public function setCostAttribute($value)
{
return $this->attributes['cost'] = // call function B;
}
public function setTotalCostAttribute($value)
{
return $this->attributes['total_cost'] = // call function B;
}
}
Controller
class ChargesController extends Controller
{
use ValidateCreditNoteCharges, Messages, ApiResponser;
public function addCharge(Request $request)
{
// $this->validateAddCharge($request);
$credit_note_charge = new Charge($request->all());
$get_result = $credit_note_charge->add();
return $this->createdResponse($get_result);
}
}
the $request is like this
{
"name":"TEST",
"quantity":1,
"cost":56.00,
"total_cost":56.00
}
The Problem is:
when I dd(gettype($value)); in getTotalCostAttribute
from the add() function the type of total_cost value is string
from the find() function the type of total_cost value is integer
in the database the data type for total_cost is int(10)
I am not sure why is the type of the return values casted into strings when use create()
I have searched for this for 3 hours now and couldn't find a solution. i need the data type correctly because accoriding to the data type i do different calculations in my calling functions A and B

How to pass an object to a view in Laravel?

I want to pass an object of Student Model to a view in Laravel. I tried using
return view('student/main')->with($student);
Where $student is a instance of Student Model (Code is below)
But it gave an error "Illegal offset type"
I know that data can be passed as an array to a view. But I really want to pass it as a object if possible. Then I can display data as follows by fetching data from get methods.
<h4 class="text-left"><strong>{{$student->getName()}}</strong> </h4>
I am looking for a solution which can be done using keeping objects instead of arrays.(if possible)
The Student model code is as follows. It consists with simply setters and getters.
class Student extends Model{
//attributes
private $student_id;
private $first_name;
private $last_name;
private $batch_id;
// set attributes
public function setID($student_id)
{
$this->student_id = $student_id;
}
public function setFirstName($first_name)
{
$this->first_name = $first_name;
}
public function setLastName($last_name)
{
$this->last_name = $last_name;
}
public function setBatchID($batch_id)
{
$this->batch_id = $batch_id;
}
// get attributes
public function getName()
{
return $this->first_name." ".$this->last_name;
}
public function getID()
{
return $this->student_id;
}
public function getBatchID()
{
return $this->batch_id;
}
You've got a number of options to do that
return view('student/main', ['student'=> $student]);
return view('student/main', compact('student'));
return view('student/main')->with('student', $student);
return view('student/main')->withStudent($student);
You have to name your variable:
return view('student/main')->with(['student' => $student]);

Custom userIdentity class in yii2

I want to create custom userIdentity class according to my specific requirements .Here the code is
<?php
namespace app\models;
use yii\web\IdentityInterface;
use app\models\dbTables\Users;
class UserIdentity implements IdentityInterface{
const ERROR_USERNAME_INVALID=3;
const ERROR_PASSWORD_INVALID=4;
const ERROR_NONE=0;
public $errorCode;
private $_id;
private $_email;
private $_role;
private $_name;
public function findIdentityById($id){
$objUserMdl = new Users;
$user = $objUserMdl::findOne($id);
$userRole = $objUserMdl->getUserRole($user->user_id);
$this->_id = $user->user_id;
$this->_email = $user->email_address;
$this->_role = $userRole;
$this->_name = $user->full_name;
return $this;
}
public function getId()
{
return $this->_id;
}
public function getName(){
return $this->_name;
}
public function getEmail(){
return $this->_email;
}
public function getRole(){
return $this->_role;
}
public static function findIdentity($id)
{
return self::findIdentityById($id);
}
public function getAuthKey()
{
throw new NotSupportedException('"getAuthKey" is not implemented.');
}
public function validateAuthKey($authKey)
{
throw new NotSupportedException('"validateAuthKey" is not implemented.');
}
public static function findIdentityByAccessToken($token, $type = null)
{
throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.');
}
}
?>
Basically I have two tables roles and users and I want to set the specific properties from both table in yii::$app->user->identity
When I call the above code the findIdentity($id) function returns error for obvious reasons stating that I cannt call $this in static funtion . How can I set the required properties in function and return the instance of userIdentity class from it ?
I recommend reading this: When to use self over $this? you are really confusing the 2.
$objUserMdl = new Users;
$user = $objUserMdl::findOne($id);
$userRole = $objUserMdl->getUserRole($user->user_id);
You are calling :: on an object, you cannot do that.
I say delete what you have done and start again, it should be much easier then what you wrote. It would take a long time to show you how to do it properly, just look in the yii2 advance template and see how they are doing it. You can use your own identity class and set up any special attributes there. Just study the yii2 code.

Yii: How to refresh or unset or reset a model?

I need to refresh, reset or unset a model;
Normally, by using a for operation, the public static $k value should change, and id does change, but the tableName model method is called only once;
the value of tablename will always be 1, because that is the fisrt value of $i;
for($i=1;$i<=100;$i++){
VillageBuildingKXSlaveM::$server_id = 1;
VillageBuildingKXSlaveM::$k = $i;
VillageBuildingKXSlaveM::model()->findAllByAttributes(array());
}
<?php
class VillageBuildingKXSlaveM extends VillageBuildingKXM {
public static function model($className = __CLASS__) {
return parent::model($className);
}
public static $server_id;
public static $slave_db;
public static $k;
public function getDbConnection() {
self::$slave_db = Yii::app()->dbx;
if (self::$slave_db instanceof CDbConnection) {
self::$slave_db->active = false;
$config = require(Yii::app()->getBasePath() . '/config/main.php');
$connectionString = $config['components']['dbx']['connectionString'];
self::$slave_db->connectionString = sprintf($connectionString, self::$server_id);
self::$slave_db->setActive(true);
return self::$slave_db;
} else
throw new CDbException(Yii::t('yii', 'Active Record requires a "db" CDbConnection application component.'));
}
public function tableName() {
return 'village_building_k' . self::$k;
}
}
Try using
VillageBuildingKXSlaveM::model()->unsetAttributes();
to unset the attributes in a model
Or you can also pass the attributes name as arguments in the method like
VillageBuildingKXSlaveM::model()->unsetAttributes($attributes);
You can call
VillageBuildingKXSlaveM::model()->tableName();

How do I store an array of objects within an object in PHP?

I would like one of the attributes of my object to be an array of another type of object.
How do I represent this (i.e. public $someObjectArray;)
What would be the syntax to add an entry to this attribute?
What would be the syntax to reference the object?
To provide some (hopefully) useful context.
Lets assume that the object is property which has some attributes one of which is a number of tenants who will have their own properties like name, age etc...
class Tenant {
// properties, methods, etc
}
class Property {
private $tenants = array();
public function getTenants() {
return $this->tenants;
}
public function addTenant(Tenant $tenant) {
$this->tenants[] = $tenant;
}
}
If the Tenant model has some sort of identifiable property (id, unique name, etc), you could factor that in to provide better accessor methods, eg
class Tenant {
private $id;
public function getId() {
return $this->id;
}
}
class Property {
private $tenants = array();
public function getTenants() {
return $this->tenants;
}
public function addTenant(Tenant $tenant) {
$this->tenants[$tenant->getId()] = $tenant;
}
public function hasTenant($id) {
return array_key_exists($id, $this->tenants);
}
public function getTenant($id) {
if ($this->hasTenant($id)) {
return $this->tenants[$id];
}
return null; // or throw an Exception
}
}

Categories