Building breadcrumbs automatically with modules - yii2

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;
}
}

Related

Symfony dynamically add form input and convert to JSON

So this is a bit hard to explain.
thing is, I have this entity
class TypeParking
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=55)
*/
private $libelle;
/**
* #ORM\Column(type="time", nullable=true)
*/
private $tempsmax;
/**
* #ORM\Column(type="date", nullable=true)
*/
private $jourdebut;
/**
* #ORM\Column(type="date", nullable=true)
*/
private $jourfin;
/**
* #ORM\Column(type="json_array", nullable=true)
*/
private $heurstravail;
/**
* #ORM\Column(type="json_array", nullable=true)
*/
private $exception;
and this is my controller:
/**
* #Route("/new", name="type_parking_new", methods={"GET","POST"})
*/
public function new(Request $request): Response
{
$typeParking = new TypeParking();
$form = $this->createForm(TypeParkingType::class, $typeParking);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($typeParking);
$entityManager->flush();
return $this->redirectToRoute('type_parking_index');
}
return $this->render('type_parking/new.html.twig', [
'type_parking' => $typeParking,
'form' => $form->createView(),
]);
}
<?php
namespace App\Form;
use App\Entity\TypeParking;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class TypeParkingType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('libelle')
->add('tempsmax')
->add('jourdebut')
->add('jourfin')
->add('heurstravail')
->add('exception')
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => TypeParking::class,
]);
}
}
See that exception field ? It's type is JSON in the database.
it must contain a name, a starting date, and ending date and starting time and ending time.
like this https://imgur.com/a/2qrz5yy
whenever I press that Plus button I can add another exception field (JQuery).
and when I submit the form this whole exception field gets parsed into a JSON and saved into the databse alongside the rest of the form.
My database: https://imgur.com/a/UonYT3W
I've been trying to get this to work for days now and I couldn't do anything.
your form type is very very minimalistic. explicitly add (sub)fields for your exception field.
<?php
namespace App\Form;
use App\Entity\TypeParking;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
// don't forget to add the types here!
use Symfony\Component\Form\Extension\Core\Type\TextType;
class TypeParkingType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('libelle')
->add('tempsmax')
->add('jourdebut')
->add('jourfin')
->add('heurstravail')
->add('exception_name', TextType::class, ['property_path' => 'exception[name]')
// add other fields of exception, look at
// https://symfony.com/doc/current/reference/forms/types.html
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => TypeParking::class,
]);
}
}
I hope this helps ...
however, the form component (property accessor) will try to get the exception, so we have to help by adding the following to the TypeParking entity class:
public function getException() {
return array_merge([
'name' => '',
// other sub-fields "empty" values
], $this->exception ?? [] // prevent array_merge from failing if exception is empty
);
}

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

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.

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();
}

DC2Type:array comment being added to field when doing a Doctrine migration diff

When I run doctrine:migrations:diff I have a unexecuted migration relating to a table and entity that I have not made changes to.
This is the SQL that is generated:
ALTER TABLE crmpicco_course_guide_row CHANGE courses courses
LONGTEXT DEFAULT NULL COMMENT \'(DC2Type:array)\'
This is the original SQL generated when mapping the entity and creating the table for the first time:
courses LONGTEXT DEFAULT NULL
My Doctrine mapping:
courses:
type: array
nullable: true
My entity:
/**
* #var array
*/
protected $courses = array();
/**
* {#inheritdoc}
*/
public function getCourses()
{
return $this->courses;
}
/**
* #param array $courses
*/
public function setCourses(array $courses)
{
$this->courses = $courses;
}
Why is it adding the comment and is there a way to prevent this from appearing during a diff?
The comment is added because of this method:
/**
* {#inheritdoc}
*/
public function requiresSQLCommentHint(AbstractPlatform $platform)
{
return true;
}
You can overwrite the existing class and use your custom class where you return false:
<?php
namespace My\DBAL\Types;
/**
* Overwrite array type to prevent comment hint
*/
class ArrayType extends \Doctrine\DBAL\Types\ArrayType
{
/**
* {#inheritdoc}
*/
public function requiresSQLCommentHint(AbstractPlatform $platform)
{
return false;
}
}
And register your custom type as follows:
'doctrine' => array(
// ...other config
'configuration' => array(
'orm_default' => array(
'types' => array(
'array' => 'My\DBAL\Types\ArrayType'
)
)
)
)