Yii 2.0 - Trying to get property of non-object - database

I try to change the "ID Kategori"(Category ID) to "Nama Kategori" (Category Name), the Category ID is in product table and has a relation to category table.
for the gridView im using kartik-v gridView
i know the error is at return Html::a($model->kategori->deskripsi ,['kategori/view','id' => $model->Id]);, But i dont know whats the problem or how to fix it
please help me... >.<
[
'label' => 'Kategori',
'attribute' => 'IdKategori',
'format' => 'raw',
'vAlign' => 'middle',
'value' => function ($model, $key, $index) {
return Html::a($model->kategori->deskripsi ,['kategori/view','id' => $model->Id]);
},
],
and this is the model
<?php
namespace common\models;
use Yii;
/**
* This is the model class for table "produk".
*
* #property integer $Id
* #property integer $IdKategori
* #property string $nama_produk
* #property integer $harga_produk
* #property string $gambar
* #property string $deksripsi_produk
* #property string $detail_produk
*/
class Produk extends \yii\db\ActiveRecord
{
/**
* #inheritdoc
*/
public $file;
public static function tableName()
{
return 'produk';
}
/**
* #inheritdoc
*/
public function rules()
{
return [
[['IdKategori', 'nama_produk', 'harga_produk', 'gambar', 'deksripsi_produk', 'detail_produk'], 'required'],
[['IdKategori', 'harga_produk'], 'integer'],
[['file'], 'file'],
[['nama_produk', 'file', 'gambar', 'deksripsi_produk', 'detail_produk'], 'string', 'max' => 255],
[['IdKategori'], 'exist', 'skipOnError' => true, 'targetClass' => Kategori::className(), 'targetAttribute' => ['IdKategori' => 'Id']],
];
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'Id' => 'ID',
'IdKategori' => 'Id Kategori',
'nama_produk' => 'Nama Produk',
'harga_produk' => 'Harga Produk',
'gambar' => 'Gambar',
'deksripsi_produk' => 'Deksripsi Produk',
'detail_produk' => 'Detail Produk',
];
}
public function getKategori()
{
return $this->hasOne(Kategori::className(), ['Id' => 'IdKategori']);
}
}
This is the kategori model
<?php
namespace common\models;
use Yii;
/**
* This is the model class for table "kategori".
*
* #property integer $Id
* #property integer $ParentId
* #property string $nama_kategori
* #property string $deskripsi
*/
class Kategori extends \yii\db\ActiveRecord
{
/**
* #inheritdoc
*/
public static function tableName()
{
return 'kategori';
}
/**
* #inheritdoc
*/
public function rules()
{
return [
[['ParentId', 'nama_kategori', 'deskripsi'], 'required'],
[['ParentId'], 'integer'],
[['nama_kategori', 'deskripsi'], 'string', 'max' => 255],
];
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'Id' => 'ID',
'ParentId' => 'Parent ID',
'nama_kategori' => 'Nama Kategori',
'deskripsi' => 'Deskripsi',
];
}
}

I checked myself to make sure I can repeat your error and it seems to be the case.
In your database, table produkt has a column called IdKategori and at least 1 row contains either null or non-existing value for table kategori (non-existing value means that it has an ID that does not exist in kategori table).
You can solve that with (one example):
'value' => function ($model, $key, $index) {
if (empty($model->kategori->deskripsi)) {
return '';
} else {
return Html::a($model->kategori->deskripsi, ['kategori/view', 'id' => $model->Id]);
}
},

Related

DebugKit Error: CakePHP get() mystery empty elements in associations

EDIT: This is after upgrading from CakePHP 4.0 to 4.3
With DebugKit turned on, CakePHP is throwing the following error message:
Argument 1 passed to Cake\ORM\Entity::get() must be of the type string, bool given, called in /Users/thomasbelknap/one-vision/vendor/cakephp/cakephp/src/Datasource/EntityTrait.php on line 607
This appears to be as a result of an empty, numerically-indexed element in the return array of a database query. Let me explain:
In my system, a User has many Estimates (and also Carts and Orders, but more on that). These two clauses in the User and Estimate table files bare this out:
$this->belongsTo('Users', [
'foreignKey' => 'user_id',
]);
::snip::
$this->hasMany('Estimates', [
'foreignKey' => 'user_id',
'dependent' => true,
]);
I am querying this information on a "Dashboard" page using the following query structure:
$user = $this->Users->get($this->getRequest()->getSession()->read('Auth.id'), [
'contain' => [
'UserGroups',
'Carts' => function ($q) {
return $q->limit(5);
},
'Orders' => function ($q) {
return $q->limit(5);
},
'Orders.OrderStatuses',
'Estimates' => function ($q) {
return $q->limit(5);
},
'Estimates.EstimateStatuses',
],
]);
I am not doing any further transformation of the data. There are no Collection functions being employed here. Nevertheless, in the estimates returned, there is always a mystery, numerically-indexed and empty array element:
[1] => Visualize\Model\Entity\Estimate Object
(
[id] => 30
[user_id] => 1
[estimate_status_id] => 5
[estimate_po] => lksjdf
[estimate_total] =>
[name] => Full throttle
[instore_date] => Cake\I18n\FrozenDate Object
(
[date] => 2022-12-31 00:00:00.000000
[timezone_type] => 3
[timezone] => EST
)
[notes] => Let's do this.
[created] => Cake\I18n\FrozenTime Object
(
[date] => 2022-04-19 13:20:39.000000
[timezone_type] => 3
[timezone] => EST
)
[modified] => Cake\I18n\FrozenTime Object
(
[date] => 2022-04-19 13:21:26.000000
[timezone_type] => 3
[timezone] => EST
)
[1] =>
[location_info] => Array
(
)
[[new]] =>
[[accessible]] => Array
(
[user_id] => 1
[estimate_status_id] => 1
[estimate_po] => 1
[estimate_total] => 1
[name] => 1
[instore_date] => 1
[notes] => 1
[created] => 1
[modified] => 1
[user] => 1
[line_items] => 1
[orders] => 1
[estimate_options] => 1
)
[[dirty]] => Array
(
)
[[original]] => Array
(
)
[[virtual]] => Array
(
[item_count] => 1
[box_count] => 1
[deliverable_count] => 1
[0] => location_info
[estimate_total] => 1
)
[[hasErrors]] =>
[[errors]] => Array
(
)
[[invalid]] => Array
(
)
[[repository]] => Estimates
)
This only affects the Estimates part of the query! The other two elements do not have this mystery index. Here's what CakePHP ultimately uses as it's query, which again, seems fine:
SELECT
Estimates.id AS Estimates__id,
Estimates.user_id AS Estimates__user_id,
Estimates.estimate_status_id AS Estimates__estimate_status_id,
Estimates.estimate_po AS Estimates__estimate_po,
Estimates.estimate_total AS Estimates__estimate_total,
Estimates.name AS Estimates__name,
Estimates.instore_date AS Estimates__instore_date,
Estimates.notes AS Estimates__notes,
Estimates.created AS Estimates__created,
Estimates.modified AS Estimates__modified,
EstimateStatuses.id AS EstimateStatuses__id,
EstimateStatuses.name AS EstimateStatuses__name,
EstimateStatuses.description AS EstimateStatuses__description,
EstimateStatuses.created AS EstimateStatuses__created,
EstimateStatuses.modified AS EstimateStatuses__modified
FROM
estimates Estimates
LEFT JOIN estimate_statuses EstimateStatuses ON EstimateStatuses.id = Estimates.estimate_status_id
WHERE
Estimates.user_id in (1)
ORDER BY
Estimates.modified DESC
LIMIT
5
For obvious reasons, I really need the DebugKit to work, but I have no idea where this mystery element is coming from. Everything seems right, to me?
Edit: Adding the definition of my estimate entity:
<?php
declare(strict_types=1);
namespace Visualize\Model\Entity;
use Cake\ORM\Entity;
/**
* Estimate Entity
*
* #property int $id
* #property int $user_id
* #property int|null $estimate_status_id
* #property string|null $estimate_po
* #property float|null $estimate_total
* #property string $name
* #property \Cake\I18n\FrozenDate $instore_date
* #property string $notes
* #property \Cake\I18n\FrozenTime $created
* #property \Cake\I18n\FrozenTime $modified
*
* #property \Visualize\Model\Entity\User $user
* #property \Visualize\Model\Entity\LineItem[] $line_items
* #property \Visualize\Model\Entity\Order[] $orders
* #property \Visualize\Model\Entity\EstimateOption[] $estimate_options
* #property float $item_count
* #property int $box_count
* #property array|\Cake\Collection\CollectionInterface|null $by_location
* #property int $deliverable_count
* #property array $location_info
* #property \Visualize\Model\Entity\EstimateStatus|null $estimate_status
* #property array|\Cake\Collection\CollectionInterface|null $by_deliverable
*/
class Estimate extends Entity
{
/**
* Fields that can be mass assigned using newEntity() or patchEntity().
*
* Note that when '*' is set to true, this allows all unspecified fields to
* be mass assigned. For security purposes, it is advised to set '*' to false
* (or remove it), and explicitly make individual fields accessible as needed.
*
* #var array
*/
protected $_accessible = [
'user_id' => true,
'estimate_status_id' => true,
'estimate_po' => true,
'estimate_total' => true,
'name' => true,
'instore_date' => true,
'notes' => true,
'created' => true,
'modified' => true,
'user' => true,
'line_items' => true,
'orders' => true,
'estimate_options' => true,
];
/**
* #var array
*/
protected $_virtual = [
'item_count' => true,
'box_count' => true,
'deliverable_count' => true,
'location_info' => true,
'estimate_total' => true,
];
/**
* Returns a zero-filled version of the cannonical ID
*
* #param int $id The autoincremental ID
* #return string
*/
protected function _getId($id): string
{
return sprintf('%05d', $id);
}
/**
* Returns the sum of quantities of all line items.
*
* #return float
*/
protected function _getItemCount(): float
{
$count = 0;
if (!empty($this->line_items)) {
$collection = new \Cake\Collection\Collection($this->line_items);
$count = $collection->sumOf('quantity');
}
return (float)$count;
}
/**
* Returns the count of locations in the estimate
*
* #return int
*/
protected function _getBoxCount(): int
{
$count = 0;
if (!empty($this->line_items)) {
$collection = new \Cake\Collection\Collection($this->line_items);
$collection = $collection->combine('location_id', 'id');
$count = $collection->count();
}
return $count;
}
/**
* Returns the total number of deliverable types in the order.
*
* #return int
*/
protected function _getDeliverableCount(): int
{
$count = 0;
if (!empty($this->line_items)) {
$collection = new \Cake\Collection\Collection($this->line_items);
$collection = $collection->combine('deliverable_id', 'id');
$count = $collection->count();
}
return $count;
}
/**
* Returns the list of locations for this estimate
*
* #return array
*/
protected function _getLocationInfo(): array
{
$info = [];
if (!empty($this->line_items)) {
$collection = new \Cake\Collection\Collection($this->line_items);
$info = $collection->combine(
'location_id',
function ($entity) {
return $entity->location;
}
);
return $info->toArray();
}
return $info;
}
/**
* Returns either the name or the id of the given estimate
*
* #return string
*/
protected function _getName(): string
{
return $this->name ?? (string)$this->id;
}
public const FIELD_ID = 'id';
public const FIELD_USER_ID = 'user_id';
public const FIELD_ESTIMATE_STATUS_ID = 'estimate_status_id';
public const FIELD_ESTIMATE_PO = 'estimate_po';
public const FIELD_ESTIMATE_TOTAL = 'estimate_total';
public const FIELD_NAME = 'name';
public const FIELD_INSTORE_DATE = 'instore_date';
public const FIELD_NOTES = 'notes';
public const FIELD_CREATED = 'created';
public const FIELD_MODIFIED = 'modified';
public const FIELD_USER = 'user';
public const FIELD_LINE_ITEMS = 'line_items';
public const FIELD_ORDERS = 'orders';
public const FIELD_ESTIMATE_OPTIONS = 'estimate_options';
public const FIELD_ITEM_COUNT = 'item_count';
public const FIELD_BOX_COUNT = 'box_count';
public const FIELD_BY_LOCATION = 'by_location';
public const FIELD_DELIVERABLE_COUNT = 'deliverable_count';
public const FIELD_LOCATION_INFO = 'location_info';
public const FIELD_ESTIMATE_STATUS = 'estimate_status';
public const FIELD_BY_DELIVERABLE = 'by_deliverable';
}
Take a close look at the dump of the entity, that field is apparently configured as an exposed virtual field, and looking at the other exposed fields, they seem to be configured incorrectly, as that property should hold a flat array of strings, not string indexed entries like 'item_count' => 1, as the value declares the field name, hence why you see a field named 1.
You are also misinterpreting the output, location_info does not belong to the 1 field, that's actually two different fields, 1 which holds something empty-ish that PHP doesn't print, like null or false, and location_info which holds an empty array.
Use debug() instead of pr()/print_r() to get a better structured dump which also properly shows empty data. And then inspect your Estimate entity class (or wherever things may be configured on the fly) and fix up the virtual fields.

write data to db in yii2

I have signup form which register a new user. But when i input data all fields exept one(username) saving in DB. I can't figure why. Help me.
here is my code from SignupForm
<?php
namespace app\modules\user\models;
use yii\base\Model;
use Yii;
/**
* Signup form
*/
class SignupForm extends Model
{
public $username;
public $email;
public $password;
public $verifyCode;
public function rules()
{
return [
['username', 'filter', 'filter' => 'trim'],
['username', 'required'],
['username', 'match', 'pattern' => '#^[\w_-]+$#i'],
['username', 'unique', 'targetClass' => User::className(), 'message' => 'This username has already been taken.'],
['username', 'string', 'min' => 2, 'max' => 255],
['email', 'filter', 'filter' => 'trim'],
['email', 'required'],
['email', 'email'],
['email', 'unique', 'targetClass' => User::className(), 'message' => 'This email address has already been taken.'],
['password', 'required'],
['password', 'string', 'min' => 6],
['verifyCode', 'captcha', 'captchaAction' => '/user/default/captcha'],
];
}
public function attributeLabels()
{
return [
'id' => 'ID',
'username' => Yii::t('app', 'USER_USERNAME'),
'email' => Yii::t('app', 'USER_EMAIL'),
'password' => Yii::t('app', 'USER_PASSWORD'),
'verifyCode' => Yii::t('app', 'USER_VERIFYCODE'),
];
}
/**
* Signs user up.
*
* #return User|null the saved model or null if saving fails
*/
public function signup()
{
if ($this->validate()) {
$user = new User();
$user->username = $this->username;
$user->email = $this->email;
$user->setPassword($this->password);
$user->status = User::STATUS_WAIT;
$user->generateAuthKey();
$user->generateEmailConfirmToken();
if ($user->save()) {
Yii::$app->mailer->compose('#app/modules/user/mails/emailConfirm', ['user' => $user])
->setFrom([Yii::$app->params['supportEmail'] => Yii::$app->name])
->setTo($this->email)
->setSubject('Email confirmation for ' . Yii::$app->name)
->send();
}
return $user;
}
return null;
}
}
code from signup(view)
<?php
use yii\captcha\Captcha;
use yii\helpers\Html;
use yii\bootstrap\ActiveForm;
/* #var $this yii\web\View */
/* #var $form yii\bootstrap\ActiveForm */
/* #var $model app\modules\user\models\SignupForm */
$this->title = Yii::t('app', 'TITLE_SIGNUP');
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="user-default-signup">
<h1><?= Yii::t('app', 'TITLE_SIGNUP') ?></h1>
<p>Please fill out the following fields to signup:</p>
<div class="row">
<div class="col-lg-5">
<?php $form = ActiveForm::begin(['id' => 'form-signup']); ?>
<?= $form->field($model, 'username') -> textInput() ?>
<?= $form->field($model, 'email') -> textInput() ?>
<?= $form->field($model, 'password')->passwordInput() ?>
<?= $form->field($model, 'verifyCode')->widget(Captcha::className(), [
'captchaAction' => '/user/default/captcha',
'template' => '<div class="row"><div class="col-lg-3">{image}</div><div class="col-lg-6">{input}</div></div>',
]) ?>
<div class="form-group">
<?= Html::submitButton('Signup', ['class' => 'btn btn-primary', 'name' => 'signup-button']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>
</div>
</div>
code from DefaultController
...
public function actionSignup()
{
$model = new SignupForm();
if ($model->load(Yii::$app->request->post())) {
if ($user = $model->signup()) {
Yii::$app->getSession()->setFlash('success', 'Подтвердите ваш электронный адрес.');
return $this->goHome();
}
}
return $this->render('signup', [
'model' => $model,
]);
}
...
code from User(model)
<?php
namespace app\modules\user\models;
use Yii;
use yii\base\NotSupportedException;
use yii\behaviors\TimestampBehavior;
use yii\db\ActiveRecord;
use yii\helpers\ArrayHelper;
use yii\web\IdentityInterface;
/**
* This is the model class for table "{{%user}}".
*
* #property integer $id
* #property integer $created_at
* #property integer $updated_at
* #property string $username
* #property string $auth_key
* #property string $email_confirm_token
* #property string $password_hash
* #property string $password_reset_token
* #property string $email
* #property integer $status
*/
class User extends ActiveRecord implements IdentityInterface
{
const SCENARIO_PROFILE = 'profile';
const STATUS_BLOCKED = 0;
const STATUS_ACTIVE = 1;
const STATUS_WAIT = 2;
public $id;
public $username;
public $password;
public $authKey;
public $accessToken;
public function rules()
{
return [
['username', 'required'],
['username', 'match', 'pattern' => '#^[\w_-]+$#i'],
['username', 'unique', 'targetClass' => self::className(), 'message' => 'This username has already been taken.'],
['username', 'string', 'min' => 2, 'max' => 255],
['email', 'required', 'except' => self::SCENARIO_PROFILE],
['email', 'email', 'except' => self::SCENARIO_PROFILE],
['email', 'unique', 'targetClass' => self::className(), 'except' => self::SCENARIO_PROFILE, 'message' => Yii::t('app', 'ERROR_EMAIL_EXISTS')],
['email', 'string', 'max' => 255, 'except' => self::SCENARIO_PROFILE],
['status', 'integer'],
['status', 'default', 'value' => self::STATUS_ACTIVE],
['status', 'in', 'range' => array_keys(self::getStatusesArray())],
];
}
public function attributeLabels()
{
return [
'id' => 'ID',
'created_at' => Yii::t('app', 'USER_CREATED'), //'Создан',
'updated_at' => Yii::t('app', 'USER_UPDATE'), //'Обновлён',
'username' => Yii::t('app', 'USER_USERNAME'), // 'Имя пользователя',
'email' => Yii::t('app', 'USER_EMAIL'), // 'Email',
'status' => Yii::t('app', 'USER_STATUS'), //'Статус',
];
}
public function scenarios()
{
return [
self::SCENARIO_DEFAULT => ['username', 'email', 'status'],
self::SCENARIO_PROFILE => ['email'],
];
}
public function behaviors()
{
return [
TimestampBehavior::className(),
];
}
/**
* #inheritdoc
*/
public static function findIdentity($id)
{
return static::findOne(['id' => $id, 'status' => self::STATUS_ACTIVE]);
}
/**
* #inheritdoc
*/
public static function findIdentityByAccessToken($token, $type = null)
{
throw new NotSupportedException('findIdentityByAccessToken is not implemented.');
}
/**
* Finds user by username
*
* #param string $username
* #return static|null
*/
public static function findByUsername($username)
{
return static::findOne(['username' => $username]);
}
/**
* #inheritdoc
*/
public function getId()
{
return $this->getPrimaryKey();
}
/**
* #inheritdoc
*/
public function getAuthKey()
{
return $this->auth_key;
}
/**
* #inheritdoc
*/
public function validateAuthKey($authKey)
{
return $this->getAuthKey() === $authKey;
}
/**
* Validates password
*
* #param string $password password to validate
* #return boolean if password provided is valid for current user
*/
public function validatePassword($password)
{
return Yii::$app->security->validatePassword($password, $this->password_hash);
}
public function getStatusName()
{
return ArrayHelper::getValue(self::getStatusesArray(), $this->status);
}
public static function getStatusesArray()
{
return [
self::STATUS_BLOCKED => 'Заблокирован',
self::STATUS_ACTIVE => 'Активен',
self::STATUS_WAIT => 'Ожидает подтверждения',
];
}
/**
* #param string $password
*/
public function setPassword($password)
{
$this->password_hash = Yii::$app->security->generatePasswordHash($password);
}
/**
* Generates "remember me" authentication key
*/
public function generateAuthKey()
{
$this->auth_key = Yii::$app->security->generateRandomString();
}
public function beforeSave($insert)
{
if (parent::beforeSave($insert)) {
if ($insert) {
$this->generateAuthKey();
}
return true;
}
return false;
}
//************************************
/**
* Finds user by password reset token
*
* #param string $token password reset token
* #return static|null
*/
public static function findByPasswordResetToken($token)
{
if (!static::isPasswordResetTokenValid($token)) {
return null;
}
return static::findOne([
'password_reset_token' => $token,
'status' => self::STATUS_ACTIVE,
]);
}
/**
* Finds out if password reset token is valid
*
* #param string $token password reset token
* #return boolean
*/
public static function isPasswordResetTokenValid($token)
{
if (empty($token)) {
return false;
}
$expire = Yii::$app->params['user.passwordResetTokenExpire'];
$parts = explode('_', $token);
$timestamp = (int) end($parts);
return $timestamp + $expire >= time();
}
/**
* Generates new password reset token
*/
public function generatePasswordResetToken()
{
$this->password_reset_token = Yii::$app->security->generateRandomString() . '_' . time();
}
/**
* Removes password reset token
*/
public function removePasswordResetToken()
{
$this->password_reset_token = null;
}
//************************************
/**
* #param string $email_confirm_token
* #return static|null
*/
public static function findByEmailConfirmToken($email_confirm_token)
{
return static::findOne(['email_confirm_token' => $email_confirm_token, 'status' => self::STATUS_WAIT]);
}
/**
* Generates email confirmation token
*/
public function generateEmailConfirmToken()
{
$this->email_confirm_token = Yii::$app->security->generateRandomString();
}
/**
* Removes email confirmation token
*/
public function removeEmailConfirmToken()
{
$this->email_confirm_token = null;
}
}
It's because you have got username attribute directly declared in User model here:
public $username;
Remove it so it can be mapped by ActiveRecord.
See the note in the guide about this.

Symfony 2 override entity field property

I want to override the entity field property. I need to get data from another database table (mapped by id). It should be a combination of "artikelnummer" and a field called "name" from another database table.
$builder->add('schlauch', 'entity', array(
'class' => 'SchlauchBundle:Artikelspezifikation',
'property' => 'artikelnummer',
'attr' => array(
'class' => 'extended-select'
),
'data_class' => null
));
The field "artikelnummer" outputs something like "12345" but I need to add the name (from another database table called "schlauch"), so it should look like "12345 Articlename". I tried a query in the entity file, but I dont't want to manipulate the output everywhere.
Is it possible to use a query for property and override it?
You can simple solve that by adding new getter to you entity:
class Artikelspezifikation
{
//…
/**
* #var Schlauch
*
* #ORM\ManyToOne(targetEntity="Schlauch", inversedBy="artikelspezifikations")
*/
private $schlauch;
//…
/**
* Get display name
*
* #return string
*/
public function getDisplayName()
{
return $this->artikelnummer . ' ' . $this->schlauch->getArtikelName();
}
//…
/**
* Set schlauch
*
* #param \SchlauchBundle\Entity\Schlauch $schlauch
*
* #return Artikelspezifikation
*/
public function setCategory(\SchlauchBundle\Entity\Schlauch $schlauch = null)
{
$this->schlauch = $schlauch;
return $this;
}
/**
* Get schlauch
*
* #return \SchlauchBundle\Entity\Schlauch
*/
public function getCategory()
{
return $this->schlauch;
}
}
And in your form class just change property:
$builder->add('schlauch', 'entity', array(
'class' => 'SchlauchBundle:Artikelspezifikation',
'property' => 'displayName',
'attr' => array(
'class' => 'extended-select'
),
'data_class' => null
));

Magento 2 adminhtml multiselect and showing selected options after save

How do I get the Magento2 Adminhtml Form with a Multiselect to select the saved options?
Here is my Adminhtml Form Class for reference.
namespace RussellAlbin\Blog\Block\Adminhtml\Post\Edit;
/**
* Adminhtml blog post edit form
*/
class Form extends \Magento\Backend\Block\Widget\Form\Generic
{
/**
* #var \RussellAlbin\Blog\Model\Category\Source\ListCategories
*/
protected $_categories;
/**
* #var \RussellAlbin\Blog\Model\Postcategory
*/
protected $_postcategory;
/**
* #var \Magento\Store\Model\System\Store
*/
protected $_systemStore;
/**
* #param \Magento\Backend\Block\Template\Context $context
* #param \Magento\Framework\Registry $registry
* #param \Magento\Framework\Data\FormFactory $formFactory
* #param \Magento\Store\Model\System\Store $systemStore
* #param \RussellAlbin\Blog\Model\Category\Source\ListCategories $categories
* #param \RussellAlbin\Blog\Model\Postcategory $postcategory
* #param array $data
*/
public function __construct(
\Magento\Backend\Block\Template\Context $context,
\Magento\Framework\Registry $registry,
\Magento\Framework\Data\FormFactory $formFactory,
\Magento\Store\Model\System\Store $systemStore,
\RussellAlbin\Blog\Model\Category\Source\ListCategories $categories,
\RussellAlbin\Blog\Model\Postcategory $postcategory,
array $data = []
) {
$this->_categories = $categories;
$this->_systemStore = $systemStore;
$this->_postcategory = $postcategory;
parent::__construct($context, $registry, $formFactory, $data);
}
/**
* Init form
*
* #return void
*/
protected function _construct()
{
parent::_construct();
$this->setId('blog_post_form');
$this->setTitle(__('Blog Post Information'));
}
/**
* Prepare form
*
* #return $this
*/
protected function _prepareForm()
{
/** #var \RussellAlbin\Blog\Model\Post $model */
$model = $this->_coreRegistry->registry('blog_post');
/** #var \Magento\Framework\Data\Form $form */
$form = $this->_formFactory->create(
['data' => ['id' => 'edit_form', 'action' => $this->getData('action'), 'method' => 'post']]
);
$form->setHtmlIdPrefix('post_');
$fieldset = $form->addFieldset(
'base_fieldset',
['legend' => __('General Information'), 'class' => 'fieldset-wide']
);
if ($model->getPostId()) {
$fieldset->addField('post_id', 'hidden', ['name' => 'post_id']);
}
// Gather our existing categories
$currentCategories = $this->_getExistingCategories( $model );
// Get all the categories that in the database
$allCategories = $this->_categories->toOptionArray();
$field = $fieldset->addField(
'blog_categories',
'multiselect',
[
'label' => __('Categories'),
'required' => true,
'name' => 'blog_categories',
'values' => $allCategories,
'value' => $currentCategories
]
);
$form->setValues($model->getData());
$form->setUseContainer(true);
$this->setForm($form);
return parent::_prepareForm();
}
/**
* #param $model
* #return array
*/
private function _getExistingCategories( $model )
{
// Get our collection
$existingCategories = $this->_postcategory->getCollection()
->addFieldToSelect('category_id')
->addFieldToFilter('post_id', $model->getId());
// Setup our placeholder for the array of categories needed to set back on the value of the multiselect
$itemList = array();
foreach($existingCategories as $_item)
{
$itemList[] = $_item['category_id'];
}
return $itemList;
}
}
I looked back at some work I have done in Magento 1.x and I the only way I got this to work before was using javascript and the setAfterElementHtml to set the options that matched. I remember hating this method of getting it accomplished because it seemed like a work around.
I found the issue.
I did not have the values saved on the model.
// This is what shows it as selected on reload
$model->setData('blog_categories', $categories);
$fieldset->addField(
'blog_categories',
'multiselect',
[
'name' => 'blog_categories[]',
'label' => __('Categories'),
'title' => __('Categories'),
'required' => true,
'values' => $optionArray,
'disabled' => false
]
);

Yii2 - foreach loop to save all files path in database model

Greetings,
i'm using a multiple file upload input in Yii2.
The multiole file input is saving very well the files selected inside a folder called audio.
The problen is i cannot inside foreach loop save the name and path of each file in the database model Faixas.php inside a field called ficheiro.
Here is my code:
Controller:
public function actionCreate()
{
$model = new Faixas();
if (Yii::$app->request->isPost) {
$model->files = UploadedFile::getInstances($model, 'files');
if ($model->upload()) {
return $this->render('create', ['model' => $model]);
}
}
return $this->render('create', ['model' => $model]);
}
The Model as a function named upload() to save the files in folder, but the part to save inside the model->ficheiro is not working (don't figure it out ?):
public function rules()
{
return [
[['nome', 'ficheiro', 'dummy1', 'dummy2', 'dummy3', 'dummy4', 'dummy5'], 'string', 'max' => 255],
[['files'], 'file', 'skipOnEmpty' => false, 'extensions' => 'mp3, ogg, webm', 'maxFiles' => 30],
];
}
public function upload()
{
$model = new Faixas();
if ($this->validate()) {
foreach ($this->files as $file) {
$file->saveAs('audio/' . $file->baseName . '.' . $file->extension);
// this part in not working, why ????
$filePath = 'audio/' . $file->baseName . '.' . $file->extension;
$model->ficheiro = $filePath;
$model->save();
}
return true;
} else {
return false;
}
}
And finally the view, which renders the input:
<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?>
<?= $form->field($model, 'files[]')->fileInput(['multiple' => true, 'accept' => 'audio/*']) ?>
<button>Submit</button>
Any ideas on the way to get it done?
Many thanks...
The entire Faixas.php model class:
<?php
namespace app\models;
use Yii;
/**
* This is the model class for table "faixas".
*
* #property integer $id
* #property string $nome
* #property string $ficheiro
* #property string $dummy1
* #property string $dummy2
* #property string $dummy3
* #property string $dummy4
* #property string $dummy5
*/
class Faixas extends \yii\db\ActiveRecord
{
/**
* #inheritdoc
*/
public static function tableName()
{
return 'faixas';
}
public $files;
/**
* #inheritdoc
*/
public function rules()
{
return [
[['nome', 'ficheiro', 'dummy1', 'dummy2', 'dummy3', 'dummy4', 'dummy5'], 'string', 'max' => 255],
[['files'], 'file', 'skipOnEmpty' => false, 'extensions' => 'mp3, ogg, webm', 'maxFiles' => 30],
];
}
public function upload()
{
if ($this->validate()) {
foreach ($this->files as $file) {
$file->saveAs('audio/' . $file->baseName . '.' . $file->extension);
$model = new Faixas();
// this part in not working, why ????
$filePath = 'audio/' . $file->baseName . '.' . $file->extension;
$model->ficheiro = $filePath;
$model->save();
}
return true;
} else {
return false;
}
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'id' => Yii::t('app', 'ID'),
'nome' => Yii::t('app', 'Nome'),
'ficheiro' => Yii::t('app', 'Ficheiro'),
'dummy1' => Yii::t('app', 'Dummy1'),
'dummy2' => Yii::t('app', 'Dummy2'),
'dummy3' => Yii::t('app', 'Dummy3'),
'dummy4' => Yii::t('app', 'Dummy4'),
'dummy5' => Yii::t('app', 'Dummy5'),
];
}
// coloca o Audio na GridView
public function getAudio()
{
return \Yii::$app->request->BaseUrl.'/'.$this->ficheiro;
}
}
Seems you create the model in wrog place. You create a $model = new Faixas(); ouuside the loop. try creating inside.
public function upload()
{
if ($this->validate()) {
foreach ($this->files as $file) {
$file->saveAs('audio/' . $file->baseName . '.' . $file->extension);
$model = new Faixas();
$filePath = 'audio/' . $file->baseName . '.' . $file->extension;
$model->ficheiro = $filePath;
$model->save();
}
return true;
} else {
return false;
}
}
SOLVED... removed from the model file the rule:
[['files'], 'file', 'skipOnEmpty' => false, 'extensions' => 'mp3, ogg, webm', 'maxFiles' => 30],
Now the foreach() loop saves every file Path in the database.
Many thanks to all that somehow tried to achieve a solution.

Resources