Yii can't save data - unknown method save() - php

Hello I am new to Yii framework and I am trying to save data to database. It won't work I don't know where is the problem.
Controller:
namespace app\controllers;
use app\models\Client;
use Yii;
use yii\web\Controller;
class ClientController extends Controller {
/**
* Displays Client_Register.
*
* #return string
*/
public function actionAdd() {
$model = new Client();
if ($model->load(Yii::$app->request->post())) {
if ($model->save()) {
return $this->refresh();
}
}
return $this->render('add', ['model' => $model,]);
}
}
View:
<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]); ?>
<?= $form->field($model, 'name')->textInput(['autofocus' => true]) ?>
<?= $form->field($model, 'lastname') ?>
<?= $form->field($model, 'birthday') ?>
<div class="form-group">
<?= Html::submitButton('Save', ['class' => 'btn btn-primary', 'name' => 'add-button']) ?>
</div>
<?php ActiveForm::end(); ?>
Model:
namespace app\models;
use yii\base\Model;
/**
* Client is the model behind the client form.
*/
class Client extends Model {
public $id;
public $name;
public $lastname;
public $birthday;
public static function tableName() {
return 'clients';
}
/**
* #return array the validation rules.
*/
public function rules() {
return [
[['name', 'lastname', 'birthday',], 'required'],
];
}
public function attributeLabels() {
return [
'id' => 'Id',
'name' => 'Name',
'lastname' => 'Last Name',
];
}
}
I have already created the database with migrations. But I don't why does this error happen. Should I include some save method in model or how can I solve this issue. I looked at other examples too. They are identical as my code. Do you have any idea where is the problem?

Your Client class extends Model, which does not support saving data in database, thus save() method is not defined. If you want to work with database record, your model should extend ActiveRecord:
class Client extends ActiveRecord {
public static function tableName() {
return 'clients';
}
public function rules() {
return [
[['name', 'lastname', 'birthday'], 'required'],
];
}
public function attributeLabels() {
return [
'id' => 'Id',
'name' => 'Name',
'lastname' => 'Last Name',
];
}
}

Related

Can't draw related model data, get invalid foreach argument

Attempting to recast a Yii 1.1 application in Yii2 advanced template, and stumbling on the display of related model data in a view.
Have searched for answers regarding this but as far as I can tell, I'm following Yii2 protocols for gathering the data, and other extended examples I've seen have all been related to e.g. GridView
Relevant Model code:
frontend/config/main.php
return [
...
'components' => [
...
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules'=> [
'home' => 'page/home',
'<alias:about>' => 'page/page',
'page/<alias>' => 'page/page'
],
],
common/models/Page.php
namespace common\models;
use Yii;
/**
* This is the model class for table "page".
...
* The followings are the available model relations:
* #property Content[] $contents
*/
class Page extends \yii\db\ActiveRecord
{
/**
* #inheritdoc
*/
public static function tableName()
{
return 'page';
}
...
public function getContents()
{
return $this->hasMany(Content::className(), ['pageId' => 'id']);
}
...
models/common/Content.php
namespace common\models;
use Yii;
/**
* This is the model class for table "content".
...
* The followings are the available model relations:
* #property Page $page
*/
class Content extends \yii\db\ActiveRecord
{
/**
* #inheritdoc
*/
public static function tableName()
{
return 'content';
}
...
public function rules()
{
return [
[['name', 'slug', 'pageId', 'content'], 'required'],
[['pageId'], 'integer'],
[['content'], 'string'],
[['name', 'slug'], 'string', 'max' => 255],
[['pageId'], 'exist', 'skipOnError' => true, 'targetClass' => Page::className(), 'targetAttribute' => ['pageId' => 'id']],
];
}
...
public function getPage()
{
return $this->hasOne(Page::className(), ['id' => 'pageId']);
}
frontend/controllers/PageController.php
class PageController extends Controller
{
...
public function actionPage($alias)
{
$model=$this->loadContent($alias);
if ($alias == "home")
{
$news=$this->loadRecentNews();
}
$this->render('default',[
'model'=>$model,
'news'=>$news
]);
}
...
public function loadContent($page)
{
$pageModel = Page::find()->where(['slug'=>$page])->with('contents')->all();
if($pageModel===null)
throw new CHttpException(404,'The requested Page could not be found.');
return $pageModel;
}
public function loadRecentNews()
{
$newsModel = News::find()->orderBy('create_time DESC')->limit(2)->all();
return $newsModel;
}
frontend/views/page/default.php (portion that fails)
<?php foreach ($model->contents as $content) { ?>
<h3><?php echo $content->name; ?></h3>
<?php echo $content->content; ?>
<?php } ?>

missing id passed in urlindent

I am trying to upload file on server and to view the file uploaded I have to pass the model id to the view action. It is all good untill I add
'options' => ['enctype'=>'multipart/form-data']
to my ActiveForm. After I add it the id passed to the action view by URL doesn't show anymore and I get missing parameter message.
i am using yii 2.0.2
Create action:
public function actionCreate()
{
$model = new SubContent();
if ($model->load(Yii::$app->request->post()) ) {
//$fileName = $model->id;
$model->save();
$fileName = $model->id;
$files =UploadedFile::getInstance($model,'files');
$files->saveAs( 'uploads/'.$files->getBaseName().'.'.$files->getExtension());
$model->files=$files;
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('create', [
'model' => $model,
]);
}
}
Form:
<div class="sub-content-form">
<?php $form = ActiveForm::begin(['options' => ['enctype'=>'multipart/form-data']]); ?>
<?= $form->field($model, 'files')->fileInput(['maxlength' => 255]) ?>
<?= $form->field($model, 'sub_id')
->dropDownList(ArrayHelper::map(Subjects::find()
->where(['year'=>\Yii::$app->user->identity->year])
->all(),'id','sub_name'),
['prompt'=>' select subject']
) ?>
<div class="form-group">
<?= Html::submitButton($model->isNewRecord ? 'Create' : 'Update', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>
and this is my model
<?php
namespace frontend\models;
use Yii;
/**
* This is the model class for table "sub_content".
*
* #property integer $id
* #property string $files
* #property integer $sub_id
*
* #property Subjects $sub
*/
class SubContent extends \yii\db\ActiveRecord
{
/**
* #inheritdoc
*/
public static function tableName()
{
return 'sub_content';
}
public function rules()
{
return [
[['files', 'sub_id'], 'required'],
[['sub_id'], 'integer'],
[['files'], 'string', 'max' => 255]
];
}
public function attributeLabels()
{
return [
'id' => 'ID',
'files' => 'Files',
'sub_id' => 'Sub ID',
];
}
/**
* #return \yii\db\ActiveQuery
*/
public function getSub()
{
return $this->hasOne(Subjects::className(), ['id' =>
'sub_id']);
}
}
edited..... still didnt work
First of all - since 2.0.8 you don't have to add enctype, it's done automatically when you add fileInput field.
You are not verifying if $model->save() returned true and it looks like that model did not pass validation and because of that $model is not saved and because of that $model->id is not set.
You should do something like:
public function actionCreate()
{
$model = new SubContent();
if ($model->load(Yii::$app->request->post()) ) {
$model->files = UploadedFile::getInstance($model,'files');
if ($model->save()) {
$model->files->saveAs('uploads/' . $files->getBaseName() . '.' . $files->getExtension());
return $this->redirect(['view', 'id' => $model->id]);
}
}
return $this->render('create', ['model' => $model]);
}
For this to work you need to set proper validation rules in SubContent model.

How to show relational data in yii2

I'm having trouble understanding how relations work in yii 2
I have 2 tables in a mysql database, author and book.
book has a column named author which links to the id of the author table via foreign key.
I've generated CRUD using gii, and I want the author name to appear in the list view, as well as dropdowns for the author name in the create and update views.
But I cant seem to get the relation working even in the list view.
Here's my code
Book Model:
<?php
namespace app\models;
use Yii;
use app\models\Author;
/**
* This is the model class for table "book".
*
* #property integer $id
* #property string $name
* #property integer $author
*/
class Book extends \yii\db\ActiveRecord
{
/**
* #inheritdoc
*/
public static function tableName()
{
return 'book';
}
/**
* #inheritdoc
*/
public function rules()
{
return [
[['name', 'author'], 'required'],
[['author'], 'integer'],
[['name'], 'string', 'max' => 11]
];
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'name' => 'Name',
'author' => 'Author',
];
}
public function getAuthor()
{
return $this->hasOne(Author::className(), ['id' => 'author']);
}
}
BookSearch Model:
<?php
namespace app\models;
use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use app\models\Book;
/**
* BookSearch represents the model behind the search form about `app\models\Book`.
*/
class BookSearch extends Book
{
/**
* #inheritdoc
*/
public function rules()
{
return [
[['id', 'author'], 'integer'],
[['name'], 'safe'],
];
}
/**
* #inheritdoc
*/
public function scenarios()
{
// bypass scenarios() implementation in the parent class
return Model::scenarios();
}
/**
* Creates data provider instance with search query applied
*
* #param array $params
*
* #return ActiveDataProvider
*/
public function search($params)
{
$query = Book::find();
$query->joinWith('author');
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
var_dump($dataProvider);
if (!$this->validate()) {
// uncomment the following line if you do not want to return any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
$query->andFilterWhere([
'id' => $this->id,
'author' => $this->author,
]);
$query->andFilterWhere(['like', 'name', $this->name]);
return $dataProvider;
}
}
Also, here's the view file:
<?php
use yii\helpers\Html;
use yii\grid\GridView;
/* #var $this yii\web\View */
/* #var $searchModel app\models\BookSearch */
/* #var $dataProvider yii\data\ActiveDataProvider */
$this->title = 'Books';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="book-index">
<h1><?= Html::encode($this->title) ?></h1>
<?php // echo $this->render('_search', ['model' => $searchModel]); ?>
<p>
<?= Html::a('Create Book', ['create'], ['class' => 'btn btn-success']) ?>
</p>
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
'id',
'name',
[
'attribute' => 'author',
'value' => 'author.name',
],
['class' => 'yii\grid\ActionColumn'],
],
]); ?>
</div>
Author Model:
<?php
namespace app\models;
use Yii;
/**
* This is the model class for table "author".
*
* #property integer $id
* #property string $name
*/
class Author extends \yii\db\ActiveRecord
{
/**
* #inheritdoc
*/
public static function tableName()
{
return 'author';
}
/**
* #inheritdoc
*/
public function rules()
{
return [
[['name'], 'required'],
[['name'], 'string', 'max' => 200]
];
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'name' => 'Name',
];
}
}
I think I may have to change something somehwhere in the author/authorSearch model.
Can someone help
thanks
You can also add columns to a gridview with value from an anonymous function as described here http://www.yiiframework.com/doc-2.0/yii-grid-datacolumn.html#$value-detail. For example you can show an author's name like this in a gridview:
<?= GridView::widget([
'dataProvider'=>$dataProvider,
'filterModel'=>$searchModel,
'columns'=>[
[
'attribute'=>'author.name',
'value'=>function ($model, $key, $index, $column) {
return $model->author->name;
},
],
//...other columns
]);
?>
you can also return a html-link to the detail-view of an author like this:
//...
'columns'=>[
[
'attribute'=>'author',
'value'=>function ($model, $key, $index, $column) {
return Html::a($model->author->name, ['/author/view', 'id'=>$model->author->id]);
},
],
//...
],
//...
You can access relation table data in any crud view file using their relation name. $model->relName->attribute_name.
And You can access relation table data in gridview at following way :
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
[
'attribute' => 'author',
'value'=>'author.author_name', //relation name with their attribute
]
],
]);
First you need a get function in your model, but you have.
This is :
public function getAuthor()
{
return $this->hasOne(Author::className(), ['id' => 'author']);
}
Now you just need do one more thing.
Go to the index file, and to GridView, and columns.
Write this into columns:
[
'attribute' => 'author',
'value' => 'author.name',
],
In the value, the first parameter is your Get function, what named is : getAuthor, and . after your column name.
Hello in your BookSearch Mode please add below code like this.
$query->joinWith(array('author'));
$query->andFilterWhere(['id' => $this->id,])
->andFilterWhere(['like', 'author.author_name', $this->author]);
And in your view file please use below given code below code is for view file inside grid attrubute.
[
'attribute' => 'author',
'value' => 'author.name',
]
i hope this will helps you. and i hope in author table name is stored in name column.
This worked for me - inside the BookSearch Model:
public function search($params)
{
$dataProvider = new \yii\data\SqlDataProvider([
'sql' => 'SELECT book.id as id ,book.author_fk, book.name as book_name , author.name as author_name' .
' FROM book ' .
'INNER JOIN author ON (book.author_fk = author.id) '
]);
$this->load($params);
if (!$this->validate()) {
// uncomment the following line if you do not want to return any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
return $dataProvider;
}
Only issue now is getting the ActionColumn to work correctly, they're currently linking to the wrong IDs, help anyone.

Yii2 - Sort and filter by one to many to one relation in GridView

I have 3 models: PurchaseOrders, PurchaseOrderItems, and Vendors. PurchaseOrders can have many PurchaseOrderItems tied to each one, and each PurchaseOrderItem can have one Vendor associated with it. My dilemma, is that I need to not only show the Vendors in the PurchaseOrder GridView, but make that column capable of being filtered and sorted. I have figured out the MySQL query to get the Vendors associated with PurchaseOrder, but am at a loss in tying it all together with Yii2.
Query:
SELECT pos.id, pos.notes, group_concat(distinct(vend.name) order by vend.name ASC SEPARATOR ', ' ) as vendorNames
FROM purchase_orders as pos
JOIN purchase_order_items as PO_item
ON pos.id = PO_item.purchase_order_id
JOIN vendors as vend
ON PO_item.vendor_id = vend.id group by pos.id
PurchaseOrder Model:
class PurchaseOrders extends \yii\db\ActiveRecord
{
public static function tableName()
{
return 'intra_purchase_orders';
}
public function getPurchaseOrderItems() {
return $this->hasMany(PurchaseOrderItems::className(['purchase_order_id' => 'id']);
}
}
PurchaseOrderItems Model:
class PurchaseOrderItems extends \yii\db\ActiveRecord
{
public function getVendor()
{
return $this->hasOne(Vendors::className(), ['id' => 'vendor_id']);
}
}
Vendors Model:
class Vendors extends \yii\db\ActiveRecord
{
public function getPurchaseOrderItems()
{
return $this->hasMany(PurchaseOrderItems::className(), ['vendor_id' => 'id']);
}
}
Is there a way to tie PurchaseOrders to Vendors, so that I can show Vendors associated with it like I'm doing in the MySQL query?
Edit
Forgot to add to the original question, I had created a function within the PurchaseOrder model to list the Vendors, however; they are not sortable in the GridView this way.
public function getVendors() {
$vendor_arry = [];
foreach ($this->purchaseOrderItems as $key => $item) {
array_push($vendor_arry, $item->vendor->name);
}
sort($vendor_arry);
return implode(array_unique($vendor_arry, SORT_STRING), ", ");
}
With the below modifications, it should work fine.
PurchaseOrder model:
<?php
class PurchaseOrders extends \yii\db\ActiveRecord
{
public $vendor_name; //This is the variable that will be used for filtering
public function rules()
{
return [
[['vendor_name'], 'string'] //Specify the variable as string
];
}
public static function tableName()
{
return 'intra_purchase_orders';
}
public function getPurchaseOrderItems() {
return $this->hasMany(PurchaseOrderItems::className(['purchase_order_id' => 'id']);
}
public function getVendors() {
$vendor_arry = [];
foreach ($this->purchaseOrderItems as $key => $item) {
array_push($vendor_arry, $item->vendor->name);
}
sort($vendor_arry);
return implode(array_unique($vendor_arry, SORT_STRING), ", ");
}
}
PurchaseOrderSearch model:
<?php
namespace {your_namespace};
use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use app\models\PurchaseOrder;
class PurchaseOrderSearch extends PurchaseOrder
{
public function rules()
{
return [
[['vendor_name'], 'safe'],
];
}
public function scenarios()
{
return Model::scenarios();
}
public function search($params)
{
$query = PurchaseOrder::find()->joinWith(['purchaseOrderItems.vendor']);
$dataProvider = new ActiveDataProvider([
'query' => $query
]);
$dataProvider->sort->attributes['vendor_name'] = [
'asc' => ['vendor.name' => SORT_ASC],
'desc' => ['vendor.name' => SORT_DESC],
];
$this->load($params);
if (!$this->validate()) {
return $dataProvider;
}
$query->andFilterWhere([
'id' => $this->id
]);
$query->andFilterWhere(['like', 'vendor.name', $this->vendor_name]);
return $dataProvider;
}
}
PurchaseOrder controller:
class PurchaseOrderController extends Controller
{
public function actionIndex()
{
$searchModel = new PurchaseOrderSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
}
And finally configure the grid view,
<?php
use yii\helpers\Html;
use app\extended\GridView;
?>
<?= GridView::widget([
'dataProvider' => $dataProvider,
'columns' => [
[
'header' => 'Vendors',
'attribute'=>'vendor_name',
'value' => function ($model, $key, $index) {
return $model->vendors;
},
]
],
]); ?>
<?= $this->render('_search', ['model'=>$searchModel]) ?>
Search view: _search.php
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
?>
<div class="search-form">
<?php $form = ActiveForm::begin([
'action' => ['index'],
'method' => 'get',
]); ?>
<?= $form->field($model, 'vendor_name') ?>
<div class="form-group actions">
<?= Html::submitButton('Search', ['class' => 'btn btn-primary']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>

How to process data in the class behavior public properties in model? (Yii2)

Model code:
<?php
namespace common\models;
use Yii;
use yii\db\ActiveRecord;
use common\components\behaviors\PageAncestorBehavior;
/**
* This is the model class for table "page".
*
* #property integer $id
* #property string $title
* #property string $title_eng
* #property string $text
* #property integer $update_ts
*
* #property PageTreepath[] $pageTreepaths
*/
class Page extends \yii\db\ActiveRecord
{
public $ancestor;
public $descendant;
/**
* #inheritdoc
*/
public static function tableName()
{
return '{{%page}}';
}
/**
* #inheritdoc
*/
public function rules()
{
return [
[['title', 'text'], 'required'],
[['text'], 'string'],
[['update_ts'], 'integer'],
[['title', 'title_eng'], 'string', 'max' => 255]
];
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'title' => Yii::t('app', 'Заголовок'),
'title_eng' => Yii::t('app', 'Транcлитерация заголовка'),
'text' => Yii::t('app', 'Текст страницы'),
'update_ts' => Yii::t('app', 'Дата и время последнего обновления'),
'ancestor' => Yii::t('app', 'Категория'),
];
}
/**
* #inheritdoc
*/
public function behaviors()
{
return [
'timestamp' => [
'class' => 'yii\behaviors\TimestampBehavior',
'attributes' => [
ActiveRecord::EVENT_BEFORE_INSERT => ['update_ts'],
ActiveRecord::EVENT_BEFORE_UPDATE => ['update_ts'],
],
],
'pageAncestor' => [
'class' => PageAncestorBehavior::className(),
'ancestor' => $this->ancestor
]
];
}
/**
* #return \yii\db\ActiveQuery
*/
public function getPageTreepaths()
{
return $this->hasMany(PageTreepath::className(), ['ancestor' => 'id']);
}
/**
* Get all pages
* #return array
*/
public static function getAllPages() {
$query = new \yii\db\Query;
$query->select('id, title')
->from('{{%page}}')
->orderBy('title');
$command = $query->createCommand();
return $command->queryAll();
}
/**
* Get list all pages for dropdown list
* #return array
*/
public static function getListAllPages() {
$data = self::getAllPages();
$result = array(0 => '-');
if (!empty($data)) {
foreach ($data as $d) {
$result[$d['id']] = $d['title'];
}
}
return $result;
}
}
Behavior code:
<?php
namespace common\components\behaviors;
use yii;
use yii\base\Behavior;
use yii\db\ActiveRecord;
use common\models\Page;
class PageAncestorBehavior extends Behavior
{
public $ancestor;
public function events()
{
return [
ActiveRecord::EVENT_BEFORE_INSERT => 'beforeInsert',
];
}
/**
* #param $event
*/
public function beforeInsert($event)
{
// --- How to get ancestor value?
//error_log("Ancestor:".$this->ancestor);
}
/**
* #return Page
*/
private function getOwner() {
return $this->owner;
}
}
View:
<?php $form = ActiveForm::begin([
'enableClientValidation'=> true,
'enableAjaxValidation'=> false,
'validateOnSubmit' => true,
'validateOnChange' => true,
'validateOnType' => true,
]); ?>
<?php echo $form->errorSummary($model); ?>
<?= $form->field($model, 'title')->textInput(['maxlength' => 255]) ?>
<?php
$redactor = yii\imperavi\Widget::widget(
[
'model' => $model,
'attribute' => 'text',
'options' => [
'minHeight' => 400,
],
]
);
$error = Html::error($model,'text', ['class' => 'help-block']); //error
?>
<?= $form->field($model, 'text', ['template' => "{$error}\n{label}\n{hint}\n{$redactor}"])->textarea();?>
<br />
<?php
// There is select for Page[ancestor]. Inf Behavior i don't recieved this.
echo $form->field($model, 'ancestor')->dropDownList($allPages);
?>
<div class="form-group">
<?= Html::submitButton(
$model->isNewRecord ? Yii::t('app', 'Create') : Yii::t('app', 'Update'),
['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']
) ?>
</div>
<?php ActiveForm::end(); ?>
Controller:
public function actionCreate()
{
$model = new Page;
$allPages = Page::getListAllPages();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('create', [
'model' => $model,
'allPages' => $allPages
]);
}
}
I obtain all data from the form, but I don't receive value of $ancestor.
I want to receive these ancestor from the form that to use them in behavior.
Sorry for my English.
Just add ancestor to your rules and try!
[['title','ancestor', 'text'], 'required'],
The variable must be public in the behavior. And in the behavior you access it with this a way $this->ancestor
In your model set pageAncestor, but the name of behavior is PageAncestorBehavior.... why?

Categories