I have table query with field id which is autoincrement primary key.
Model
class Query extends \yii\db\ActiveRecord
{
public static function tableName()
{
return '{{%query}}';
}
public function rules()
{
return [
[['id', 'created_at'], 'integer'],
[['data'], 'string'],
];
}
}
Application
$q = new Query();
$q->created_at = time();
$q->data = Json::encode($query);
if ($q->save())
echo $q->id == null ? "null" : $q->id;
else
echo "Validation error";
Result is null, though new record with incremented id does occur in database.
Also I see only insert record in db log, but no record to obtain inserted row id.
What's wrong?
as you can see in vendor/yiisoft/yii2/db/ActiveRecord.php
there is already a primaryKey method
public static function primaryKey()
{
return static::getTableSchema()->primaryKey;
}
that return the proper primaryKey for the tableSchema
so you should not redefine this method ..
the method is specifically called primaryKey() not getPrimaryKey() .. this last one is the magic getter .. and return a value for var $primaryKey ..
if no insert is performed could be you have some validation problems .. try (just for debugging) using save(false)
Related
I need to declare properties inside an Active Record model. I want to use PHP 8 syntax. So I did:
final class Post extends ActiveRecord
{
public ?int $id = null;
public ?int $user_id = 0;
public string $slug;
public ?string $title = null;
public ?string $content = null;
public ?string $created_at = null;
public ?string $updated_at = null;
public ?string $published_at = null;
public static function tableName(): string
{
return 'posts';
}
public function rules(): array
{
return [
[['user_id', 'title', 'content'], 'required'],
[['user_id'], 'integer'],
[['content'], 'string'],
[['slug', 'created_at', 'updated_at', 'published_at'], 'safe'],
[['title'], 'string', 'max' => 512],
];
}
}
But now all the fields become inaccessible. When I remove them as class fields, everything is ok.
As we can see Yii just removes these model attributes since I declared them as class properties:
SQLSTATE[HY000]: General error: 1364 Field 'user_id' doesn't have a
default value The SQL being executed was: INSERT INTO posts (id)
VALUES (DEFAULT)
In most active record models in yii2 you put comments to get IDE-support like autocompletion since yii2 is using magic methods to resolve the fields to columns.
See Yii2 BaseActiveRecord all the columns are stored in a field _attributes.
So i think the best way you could do is add the doc comments as the following example to your ActiveRecord classes:
/**
* #property ?int id
* #property string slug
* #property-read Author[] authors
*/
note the #property-read indicates a relation and there is a subtile difference when accessing $object->authors; vs $object->getAuthors();. In the first case the relation query is executed and the results are returned as array (via magic methods) and in the second case you get back the raw query that is not executed yet.
And to keep with the authors example the relation method would look like the following:
public function getAuthors(): ActiveQuery
{
return $this
->hasMany(Author::class, ['id' => 'author_id'])
->viaTable('post_author', [ 'post_id' => 'id' ]);
}
Good morning dear community,
I'm new to Yii2 and working on a user login program but i get a database Exception why i try to login due to the primary key for user table
Here my code at vendor/yiisoft/yii2/db/BaseActiveRecord.php
Thank you in advance for the valuable help
* is composite or `$asArray` is `true`. A string is returned otherwise (null will be returned if
* the key value is null).
* #throws Exception if the AR model does not have a primary key
*/
public function getOldPrimaryKey($asArray = false)
{
$keys = $this->primaryKey();
if (empty($keys)) {
throw new Exception(get_class($this) . ' does not have a primary key. You should either define a primary key for the corresponding table or override the primaryKey() method.');
}
if (!$asArray && count($keys) === 1) {
return isset($this->_oldAttributes[$keys[0]]) ? $this->_oldAttributes[$keys[0]] : null;
}
$values = [];
foreach ($keys as $name) {
$values[$name] = isset($this->_oldAttributes[$name]) ? $this->_oldAttributes[$name] : null;
}```
For everybody that gonna face this kind of error
here is the solution
public static function primaryKey()
{
return ["id"];
}
I need to be able to convert a hasMany() relation, which queries and return an array into a hasOne() relation which returns object|null.
Use case:
public function getItems() : \yii\db\ActiveQuery {
return $this->hasMany(Item::class, ['parent_id' => 'id']);
}
I want to create a relation which returns one specific Item object (or null if it does not exist).
I would like to do something like this:
public function getPrimaryItem() : \yii\db\ActiveQuery {
return $this->getItems()->andWhere(["primary"=>true])->toHasOne();
}
Please do not tell me to call ->one() on the original query, because that is not going to solve the problem. I need to be able to:
call $model->primaryItem and receive either Item or null
call $model->getPrimaryItem() and receive the relation's ActiveQuery
You can toggle it by multiple property of \yii\db\ActiveQuery
public function getPrimaryItem() : \yii\db\ActiveQuery {
$query = $this->getItems();
$query->multiple = false;
//Your logics
$query->andWhere(["primary"=>true])
return $query;
}
I have a customer table which has a field called 'policy_id', where policy_id points to policy table. It is a null-able field, ie. Some customers may not have a policy.
I have a relationship code like this in Customer.php
public function policy() {
return $this->hasOne('App\Models\Policy', "id", "policy_id");
}
But when I issue a search request I am getting error like this:
Illuminate\Database\Eloquent\ModelNotFoundException: No query results for model [App\Models\Policy]
If I modify the function like this:
public function policy() {
if ($this->getAttribute('policy_id')) {
return $this->hasOne('App\Models\Policy', "id", "policy_id");
} else {
return null
}
}
But I am getting an error like this:
Call to a member function getRelationExistenceQuery() on null
Here is my search code:
$c = new Customer();
return Customer::doesntHave('policy')->orWhere(function (Builder $query) use ($req) {
$query->orWhereHas('policy', function (Builder $query) use ($req) {
$p = new Policy();
$query->where($req->only($p->getFillable()))
->orWhereBetween("policy_period_from", [$req->policy_period_start_from, $req->policy_period_start_to])
->orWhereBetween("policy_period_to", [$req->policy_period_end_from, $req->policy_period_end_to])
->orWhereBetween("payment_date", [$req->payment_date_from, $req->payment_date_to]);
});
})->where($req->only($c->getFillable()))->get();
Am I missing something or are there any other ways to do this?
PS: While debugging the above search code is returning successfully, but the exception happening from somewhere inside Laravel after the prepareResponse call.
Thanks in advance.
return $this->hasOne('App\ModelName', 'foreign_key', 'local_key');
Change the order, put the foreign_key policy_id in front of id
In your Customer Model, you need to use belongsTo method:
public function policy() {
return $this->belongsTo('App\Models\Policy', "policy_id", "id");
}
And In your Policy Model, use hasOne:
public function customer() {
return $this->hasOne('App\Models\Customer', "policy_id", "id");
}
First of all, you placed the wrong params.
$this->belongsTo('App\Models\Policy', "FK", "PK");
public function policy() {
return $this->belongsTo('App\Models\Policy','policy_id', 'id');
}
And for null value of policy_id you can use withDefault();
public function policy() {
return $this->belongsTo('App\Models\Policy','policy_id', 'id')->withDefault([
'name' => 'test'
]);;
}
there's a number of problems there but can you perhaps specify the namespace and the class of both your models - Customer and Policy.
By default, the models you create with php artisan make:model will use the \App namespace e.g. \App\Customer and \App\Policy.
Just double check that.
Also, with regards to the relationship, if the Laravel conventions have been followed you could just:
In the Customer model
public function policy() {
return $this->belongsTo(Policy::class);
}
In the Policy model
public function customer() {
return $this->hasOne(Customer::class);
}
of if a multiple customers can be under one policy
public function customers() {
return $this->hasMany(Customer::class);
}
Good luck
I create a core class named MY_Model that extends CI_Model. In this class, I create a method chaining to get all record with pagination like this :
// Take record with paging.
public function get_all_paged()
{
// get argument that passed
$args = func_get_args();
// get_all_paged($offset)
if (count($args) < 2) {
$this->get_real_offset($args[0]);
$this->db->limit($this->_per_page, $this->_offset);
}
// get_all_paged(array('status' => '1'), $offset)
else {
$this->get_real_offset($args[1]);
$this->db->where($args[0])->limit($this->_per_page, $this->_offset);
}
// return all record
return $this->db->get($this->_tabel)->result();
}
So , I just used like this on my controller,
for example
public function index($offset = NULL) {
$karyawan = $this->karyawan->get_all_paged($offset); //get all
}
I am realy confuse to get all record using join, I know join in CI like this :
public function get_all_karyawan() {
$this->db->select('tb_1 , tb_2');
$this->db->from('tb_1');
$this->db->join('tb_2', "where");
$query = $this->db->get();
return $query->result();
}
How to make it into chain in MY_Model?
Any help it so appreciated ...
The good thing in query builder, you can chain your db methods, till get(). So you can define, selects, where queries, limits in different ways.
For example:
public function category($category)
{
$this->db->where('category_id', $category);
return $this;
}
public function get_posts()
{
return $this->db->get('posts')->result();
}
And you can get all posts:
$this->model->get_posts();
Or by category:
$this->model->category(2)->get_posts();
So upon this, in your model:
public function get_all_karyawan() {
$this->db->select('tb_1 , tb_2');
$this->db->join('tb_1', "where");
// Here you make able to chain the method with this
return $this;
}
In your controller:
public function index($offset = NULL) {
$karyawan = $this->karyawan->get_all_karyawan()->get_all_paged($offset);
}