Yii2 Multilingual. Can't reach the fields from the translation table - yii2

I installed OmgDef/Multilingual via composer. Did everything step by step from the guide. The error that I get now is:
Getting unknown property: backend\models\PageAdminSearch::title
I added joinWith('translation') but nothing changes.This is my PageAdmin model and PageAdminSearch
PageAdmin:
<?php
namespace backend\models;
use omgdef\multilingual\MultilingualBehavior;
use omgdef\multilingual\MultilingualQuery;
use Yii;
/**
* This is the model class for table "page_admin".
*
* #property int $id
* #property int $id_in
* #property int $enable
* #property string $icon
*/
class PageAdmin extends \yii\db\ActiveRecord
{
public static function find()
{
return new MultilingualQuery(get_called_class());
}
public function behaviors()
{
$allLanguages = [];
foreach (Yii::$app->params['languages'] as $title => $language) {
$allLanguages[$title] = $language;
}
return [
'ml' => [
'class' => MultilingualBehavior::className(),
'languages' => $allLanguages,
//'languageField' => 'language',
//'localizedPrefix' => '',
//'requireTranslations' => false',
//'dynamicLangClass' => true',
//'langClassName' => PostLang::className(), // or namespace/for/a/class/PostLang
'defaultLanguage' => Yii::$app->params['languageDefault'],
'langForeignKey' => 'page_id',
'tableName' => "{{%page_adminlang}}",
'attributes' => [
'title',
'content',
]
],
];
}
/**
* #inheritdoc
*/
public static function tableName()
{
return 'page_admin';
}
/**
* #inheritdoc
*/
public function rules()
{
return [
[['icon'], 'string'],
[['id_in', 'enable'], 'integer']
];
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'id' => Yii::t('app', 'ID'),
'id_in' => Yii::t('app', 'Id In'),
'icon' => Yii::t('app', 'Icon'),
];
}
}
PageAdminSearch:
<?php
namespace backend\models;
use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use backend\models\PageAdmin;
/**
* PageAdminSearch represents the model behind the search form of `backend\models\PageAdmin`.
*/
class PageAdminSearch extends PageAdmin
{
/**
* #inheritdoc
*/
public function rules()
{
return [
[['id', 'id_in'], 'integer'],
];
}
/**
* #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 = PageAdmin::find()->joinWith('translations');
// add conditions that should always apply here
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
$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;
}
// grid filtering conditions
$query->andFilterWhere([
'id' => $this->id,
'id_in' => $this->id_in,
]);
$query->andFilterWhere(['like', 'title', $this->title]);
return $dataProvider;
}
}
languageDefault is bg. Did someone have the same problem? The explanation is not pretty big but I think the problem is clear enough :) Appreciate every advice!

Haven't used it tho but looking at your code you are adding a joinWith inside the search() function in the model, are you searching any field with name title inside the translations table using some gridview or search form
If that is so you need to declare a custom attribute inside your searchModel and add it to the safe rules and then use it because you are getting an error at the line
$query->andFilterWhere(['like', 'title', $this->title]);
so add a custom attribute on top of your PageAdminSearch
public $title
And it is always good to use an alias for the relation
$query = PageAdmin::find()
->joinWith(['translations'=>function($q){
$q->from('{{%transalations}} tr');
}]);
then update your rules to the following
/**
* #inheritdoc
*/
public function rules()
{
return [
[['id', 'id_in'], 'integer'],
[['title'],'safe'],
];
}
and change the line to the following
$query->andFilterWhere(['like', 'tr.title', $this->title]);
Now run it won't show you the error.

Related

Upload the file to a folder and saving the name to the database in yii2

Good afternoon!
There is a question about the file upload to yii2. There are two folders in it that will store the original and thumbnail image. At me files are loaded but here the name of a file does not load in a database
Model
namespace app\models;
use yii\base\Model;
use yii\db\ActiveRecord;
use yii\web\UploadedFile;
use yii\imagine\Image;
use Imagine\Image\Box;
/**
* This is the model class for table "images".
*
* #property integer $id
* #property string $original_image
* #property string $prev_image
*/
class Images extends ActiveRecord
{
public $imageFile;
public $file_name;
/**
* #inheritdoc
*/
public static function tableName()
{
return 'images';
}
/**
* #inheritdoc
*/
public function rules()
{
return [
[['prev_image'], 'string', 'max' => 255],
[['original_image'], 'string'],
[['imageFile'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg', 'maxSize' => 1024 * 1024 * 7],
];
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'original_image' => 'Original Image',
'prev_image' => 'Prev Image',
];
}
public function upload()
{
$temp=substr(md5(microtime() . rand(0, 9999)), 0, 20);
if ($this->validate()) {
$this->imageFile->saveAs('uploads/original/'.$temp.$this->imageFile->baseName . '.' . $this->imageFile->extension);
$imagine = Image::getImagine();
$image = $imagine->open('uploads/original/' . $temp.$this->imageFile);
$image->resize(new Box(250, 150))->save('uploads/prev/' . $temp.$this
->imageFile->baseName . '.' . $this->imageFile->extension, ['quality' => 70]);
$this->file_name=$temp.$this->imageFile->baseName . '.' . $this->imageFile->extension;
return true;
} else {
return false;
}
}
}
Controller
namespace app\controllers;
use app\models\Images;
use Yii;
use yii\web\UploadedFile;
class ImageController extends \yii\web\Controller
{
public function actionUpload()
{
$model = new Images();
if ($model->load(Yii::$app->request->post())) {
$model->imageFile = UploadedFile::getInstance($model, 'imageFile');
$model->prev_image=$model->file_name;
$model->original_image=$model->file_name;
$model->save();
if ($model->upload()) {
return;
}
}
return $this->render('upload', ['model' => $model]);
}
}
A question how to save a file name in database? Thank you in advance
It's because save happens before uplaod action, but you only define file_name in upload function. Save is what saves it in to the database.
Controller should look like this:
namespace app\controllers;
use app\models\Images;
use Yii;
use yii\web\UploadedFile;
class ImageController extends \yii\web\Controller
{
public function actionUpload(){
$model = new Images();
if ($model->load(Yii::$app->request->post())) {
$uploadedFile = UploadedFile::getInstance($model, 'imageFile');
$model->imageFile = $uploadedFile;
$model->prev_image = $uploadedFile->name
$model->original_image = $uploadedFile->name
$model->save();
if ($model->upload()) {
return;
}
}
return $this->render('upload', ['model' => $model]);
}
}

Yii2 - make an active form to take its old data when left blank

I'm trying to make password changing function, by making a form for user to type in, then it will be hashed and added to password_hash record. To prevent it being populated with previous password, I added 'value'=>'' to clear it out.
But I have a problem with this: if I leave the field blank like its default state, the password_hash field in database will be blank, too. How can I make an exception that if this field is blank, then it will automatically take the old value in database, and if it has data inputted, it will take that value instead?
<?= $form->field($model, 'password')->passwordInput(['value'=>'']) ?>
EDIT: This is my controller file:
<?php
namespace app\controllers;
use app\models\User;
use Yii;
use app\models\UserList;
use app\models\UserlistSearch;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
/**
* UserlistController implements the CRUD actions for UserList model.
*/
class UserlistController extends Controller
{
/**
* #inheritdoc
*/
public function behaviors()
{
return [
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['POST'],
],
],
];
}
/**
* Lists all UserList models.
* #return mixed
*/
public function actionIndex()
{
$searchModel = new UserlistSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* Displays a single UserList model.
* #param integer $id
* #return mixed
*/
public function actionView($id)
{
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
public function actionDomains($id)
{
return $this->render('domains', [
'model' => $this->findModel($id),
]);
}
/**
* Creates a new UserList model.
* If creation is successful, the browser will be redirected to the 'view' page.
* #return mixed
*/
public function actionCreate()
{
$model = new UserList();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('create', [
'model' => $model,
]);
}
}
/**
* Updates an existing UserList model.
* If update is successful, the browser will be redirected to the 'view' page.
* #param integer $id
* #return mixed
*/
public function actionUpdate($id)
{
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post()) ) {
$model->password_hash=Yii::$app->getSecurity()->generatePasswordHash($model->password_hash);
$model->save();
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('update', [
'model' => $model,
]);
}
}
/**
* Deletes an existing UserList model.
* If deletion is successful, the browser will be redirected to the 'index' page.
* #param integer $id
* #return mixed
*/
public function actionDelete($id)
{
$this->findModel($id)->delete();
return $this->redirect(['index']);
}
/**
* Finds the UserList model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be thrown.
* #param integer $id
* #return UserList the loaded model
* #throws NotFoundHttpException if the model cannot be found
*/
protected function findModel($id)
{
if (($model = UserList::findOne($id)) !== null) {
return $model;
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}
}
if(!empty($_POST['UserList']['password']){
---
}
Try:
public function actionUpdate($id)
{
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post()) ) {
if(!empty($_POST['UserList']['password']){
$model->password_hash=Yii::$app->getSecurity()->generatePasswordHash($_POST['UserList']['password']);
}
$model->save();
}

How to add a rule that checks if date given is past 6 days old?

I'm using yii2 basic, i'm trying to add a rule which will compare the date given by the user and give and error if it's older than 6 days.
How would i go about doing this? I'm using datepicker to input the date right now, the format i'm using is: format' => 'd-mm-yyyy
Model.php
<?php
namespace app\models;
use Yii;
/**
* This is the model class for table "invoices".
*
* #property integer $id
* #property string $invoice_number
* #property string $invoice_loadamount
* #property string $invoice_date
* #property integer $archive_id
* #property string $DateProcessed
*
* #property Archive $archive
*/
class Invoices extends \yii\db\ActiveRecord
{
/**
* #inheritdoc
*/
public static function tableName()
{
return 'invoices';
}
/**
* #inheritdoc
*/
public function rules()
{
return [
[['invoice_number', 'invoice_loadamount', 'invoice_date'], 'required'],
[['archive_id', 'invoice_number', 'invoice_loadamount'], 'integer'],
[['DateProcessed'], 'safe'],
[['invoice_number', 'invoice_loadamount', 'invoice_date'], 'string', 'max' => 100],
];
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'invoice_number' => 'Invoice Number',
'invoice_loadamount' => 'Invoice Loadamount',
'invoice_date' => 'Invoice Date',
'archive_id' => 'Archive ID',
'DateProcessed' => 'Date Processed'];
}
/**
* #return \yii\db\ActiveQuery
*/
public function getArchive()
{
return $this->hasOne(Archive::className(), ['id' => 'archive_id']);
}
}
You can use the core date validator with:
min property specifying the lower limit of the accepted date-range.
format property to indicate which date-format is used
Add something along these lines:
[['DateProcessed'], 'date', 'format' => "dd-MM-yyyy", 'min' => date("d-m-Y", strtotime('-6 days'))],

yii2 adding '0=1' to query on many to many relation

I have a project where users must be connected with each others - like friends. So i decided to connect them by the table 'connect'. Looks like it's rights. But when i tried to search for a friend requests i got the error, described below.
Here is my model User:
<?php namespace common\models
use dektrium\user\models\User as BaseUser;
use Yii;
use yii\data\ActiveDataProvider;
use yii\helpers\ArrayHelper;
use dosamigos\taggable\Taggable;
use yii\db\ActiveQuery;
/**
* User model
*
* #inheritdoc
* #property string $search
* #property string $category
*
* #property ActiveQuery $requests
*/
class User extends BaseUser
{
#region Properties
public $category;
public $search;
const SCENARIO_CATEGORY = 'category';
const SCENARIO_SEARCH = 'search';
#endregion
#region Yii
/**
* #inheritdoc
*/
public static function tableName()
{
return '{{%user}}';
}
/**
* #inheritdoc
*/
public function rules()
{
return array_merge(parent::rules(), [
[['category'], 'safe', 'on' => self::SCENARIO_CATEGORY],
[['search'], 'safe', 'on' => self::SCENARIO_SEARCH],
]);
}
/**
* #inheritdoc
*/
function scenarios()
{
return array_merge(parent::scenarios(), [
self::SCENARIO_CATEGORY => ['category'],
self::SCENARIO_SEARCH => ['search']
]);
}
#endregion
#region Callbacks
function afterFind()
{
$this->category = implode(', ', ArrayHelper::map($this->getCategories()->asArray()->all(), 'id', 'name'));
parent::afterFind();
}
/**
* #inheritdoc
*/
public function behaviors()
{
return array_merge(parent::behaviors(), [
[
'class' => Taggable::className(),
'attribute' => 'category',
'relation' => 'categories',
]
]);
}
#endregion
#region Relations
/**
* #return \yii\db\ActiveQuery
*/
function getCategories()
{
return $this->hasMany(Category::className(), ['id' => 'category_id'])->viaTable('{{%category_user}}', ['user_id' => 'id']);
}
/**
* #return \yii\db\ActiveQuery
*/
function getRequests()
{
return $this->hasMany(User::className(), ['id' => 'user_two'])->viaTable(Connection::tableName(), ['user_one' => 'id']);
}
#endregion
#region Methods
function search($params)
{
$query = self::find();
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
$this->load($params);
// if (!$this->validate()) {
// $query->where('0=1');
// return $dataProvider;
// }
$query->joinWith(['categories']);
$query->andFilterWhere(['like', 'category.name', $this->search]);
return $dataProvider;
}
function isConnected($user_id)
{
return Connection::isConnected(Yii::$app->user->id, $user_id);
}
function requestCount()
{
return Connection::requestCount(Yii::$app->user->id);
}
function requestPends()
{
$query = $this->getRequests();
$result = new ActiveDataProvider([
'query' => $query
]);
$query->joinWith(['requests']);
$query->from(User::tableName() . ' u1');
$query->where = "";
$query->andFilterWhere(['connection.status' => Connection::STATUS_PENDED]);
return $result;
}
#endregion
}
Here is my connection model:
namespace common\models;
use Yii;
use yii\data\ActiveDataProvider;
use yii\db\Query;
/**
* This is the model class for table "connection".
*
* #property integer $id
* #property integer $user_one
* #property integer $user_two
* #property integer $status
*
* #property User $userOne
* #property User $userTwo
*/
class Connection extends \yii\db\ActiveRecord
{
const STATUS_PENDED = 0;
const STATUS_ACCEPTED = 1;
const STATUS_DENIED = 2;
public static function primaryKey()
{
return array('id');
}
/**
* #inheritdoc
*/
public static function tableName()
{
return 'connection';
}
/**
* #inheritdoc
*/
public function rules()
{
return [
[['user_one', 'user_two', 'status'], 'integer'],
[['user_one'], 'exist', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['user_one' => 'id']],
[['user_two'], 'exist', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['user_two' => 'id']],
];
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'id' => Yii::t('app', 'ID'),
'user_one' => Yii::t('app', 'User One'),
'user_two' => Yii::t('app', 'User Two'),
'status' => Yii::t('app', 'Status'),
];
}
/**
* #return \yii\db\ActiveQuery
*/
public function getUserOne()
{
return $this->hasOne(User::className(), ['id' => 'user_one']);
}
/**
* #return \yii\db\ActiveQuery
*/
public function getUserTwo()
{
return $this->hasOne(User::className(), ['id' => 'user_two']);
}
function search($params)
{
$query = self::find();
$result = new ActiveDataProvider([
'query' => $query,
]);
$this->load($params);
// if (!$this->validate()) {
// $query->where('0=1');
// return $result;
// }
return $result;
}
static function isConnected($user_one, $user_two)
{
return self::find()->where('(user_one=:one AND user_two=:two) OR ((user_one=:two AND user_two=:one))', [':one' => $user_one, ':two' => $user_two])->andFilterWhere(['status' => Connection::STATUS_ACCEPTED])->count();
}
static function requestCount($user_id)
{
return (int)self::find()->where(['user_two' => $user_id])->andFilterWhere(['status' => Connection::STATUS_PENDED])->count();
}
}
request view:
<div class="panel panel-default">
<div class="panel-body">
<?php
// if ($mdlUser->requestCount()) {
echo ListView::widget([
'dataProvider' => $mdlUser->requestPends(),
'itemView' => '_list',
]);
// }
?>
</div>
</div>
Controller:
function actionFriendRequest()
{
/** #var User $mdlUser */
$mdlUser = Helper::findModel('\common\models\User', Yii::$app->user->id);
return $this->render('request', [
'mdlUser' => $mdlUser
]);
}
But yii2 make query like
`SELECT `u1`.* FROM `user` `u1` LEFT JOIN `connection` ON `u1`.`id` = `connection`.`user_one` LEFT JOIN `user` ON `connection`.`user_two` = `user`.`id` WHERE ((`connection`.`status`=0)) AND (0=1) LIMIT 20`
Question: Where did the (0=1) came from???
Take a look at your expression:
$mdlUser = Helper::findModel('\common\models\User', Yii::$app->user->id);
Most likely your 0=1 comes from Helper class, findModel method, which you haven't shown here.
UPDATE:
There are two more places where 0=1 can come from: QueryBuilder.php line 1077 and line 1226, which is the way yii2 handles empty strings/arrays for LIKE and IN conditions.

Building breadcrumbs automatically with modules

My web page has multiple modules, and i want to know if there is a way to add breadcrumbs the modules home url, without have to add manually to all files.
ex:
Home > MyModule > MyController
The question is definetely too broad, but here is solution I used:
<?php
namespace backend\modules\tests\components;
use yii\helpers\ArrayHelper;
use yii\web\Controller as BaseController;
use yii\web\View;
class Controller extends BaseController
{
/**
* #inheritdoc
*/
public function beforeAction($action)
{
if (parent::beforeAction($action)) {
Yii::$app->view->on(View::EVENT_BEGIN_BODY, function () {
$this->fillBreadcrumbs();
});
return true;
} else {
return false;
}
}
/**
* Fill common breadcrumbs
*/
protected function fillBreadcrumbs()
{
$breadcrumbs = [];
// Add needed elements to $breadcrumbs below
$label = 'Tests';
$breadcrumbs[] = $this->route == '/tests/default/index' ? $label : [
'label' => $label,
'url' => ['/tests/default/index'],
];
// ...
$this->mergeBreadCrumbs($breadcrumbs);
}
/**
* Prepend common breadcrumbs to existing ones
* #param array $breadcrumbs
*/
protected function mergeBreadcrumbs($breadcrumbs)
{
$existingBreadcrumbs = ArrayHelper::getValue($this->view->params, 'breadcrumbs', []);
$this->view->params['breadcrumbs'] = array_merge($breadcrumbs, $existingBreadcrumbs);
}
}
As you can see, it's based on view events and custom controller (extending from yii\web\Controller).
All you need next is to extend needed controllers from custom one.
Note for Pjax Users
If you are using Pjax on index views etc. ensure you add the 'data-pjax'=>'0' option as follows or the request will refresh the new context in your existing view:
Html::a('AnotherView',
['another-view', 'id' => $model->$id], [
'data-pjax' => '0', // !important when using pjax.
]);
app/config/main.php
'modules' => [
/*
* Admin => [...], // in common for frontend/backend access.
*/
'estimator' => [
'class' => 'your\namespace\Module',
'defaultUrl' => '/estimator/',
'defaultUrlLabel' => 'Estimator',
//'layout' => 'left-menu',
//'mainLayout' => '#app/views/layouts/main.php',
],
],
your-app/Module.php
<?php
namespace yourapp\namespace;
use Yii;
use yii\helpers\Inflector;
use yii\helpers\ArrayHelper;
/**
* Entry for module...
*/
class Module extends \yii\base\Module
{
/**
* #var string Layout applied for views in module.
* Comment out $layout to use default parent layout.
* When $layout is enabled, the mainLayout will be utilized instead of the default layout for the site.
*/
//public $layout = "top-menu.php"; // comment out to disable module layout.
/**
* #var string Main layout using for module. Default to layout of parent module.
* The mainLayout is used when `layout` set to 'top-menu'.
*/
//public $mainLayout = '#frontend/views/layouts/main.php';
/**
* #inheritdoc
*/
public $defaultRoute = 'project';
/**
* #var string Default url for breadcrumb
*/
public $defaultUrl;
/**
* #var string Default url label for breadcrumb
*/
public $defaultUrlLabel;
/**
* #inheritdoc
*/
public function beforeAction($action)
{
if (parent::beforeAction($action)) {
/* #var $action \yii\base\Action */
$view = $action->controller->getView();
$view->params['breadcrumbs'][] = [
'label' => ($this->defaultUrlLabel ?: 'Warehouse'),
'url' => ['/' . ($this->defaultUrl ?: $this->uniqueId)],
];
$view->params['breadcrumbs'][] = [
'label' => Inflector::pluralize(Inflector::camelize($action->controller->id)),
'url' => ['/' . ($this->defaultUrl ?: $this->uniqueId) . '/' . $action->controller->id],
];
$view->params['breadcrumbs'][] = [
'label' => Inflector::camelize($action->id),
];
return true;
}
return false;
}
}