When I try to upload multiple files, I can't do this
Model File:
namespace app\models;
class ImageFiles extends \yii\base\Model
{
public $imageFiles;
public function rules()
{
return [[
['imageFiles'],
'file',
'skipOnEmpty'=>false,
'extensions'=>'jpg, png',
'maxFiles'=>4
]];
}
}
View File
use yii\widgets\ActiveForm;
$form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]);
echo $form->field($model, 'imageFiles[]')->fileInput(['multiple'=>true, 'accept'=>'image/*']);
echo \yii\helpers\Html::submitButton('Submit',['class'=>'btn btn-success']);
ActiveForm::end();
Controller File
namespace app\controllers;
use Yii;
class ImageFilesController extends \yii\web\Controller
{
public function actionIndex()
{
$model = new \app\models\ImageFiles();
if(Yii::$app->request->isPost){
$model->imageFiles = \yii\web\UploadedFile::getInstance($model, 'imageFiles');
print_r($model->imageFiles);
return;
}
return $this->render('index', ['model'=>$model]);
}
}
print_r($model->imageFiles) show me nothing and no errors, because $model->imageFiles has no value. but when I use print_r($_FILES) It prints all images details. I got confused I did what the Yii documentation said but it is not working.
For multiple files use UploadedFile::getInstances()
class ImageFilesController extends \yii\web\Controller
{
public function actionIndex()
{
$model = new \app\models\ImageFiles();
if(Yii::$app->request->isPost){
$model->imageFiles = \yii\web\UploadedFile::getInstances($model, 'imageFiles');
print_r($model->imageFiles);
return;
}
return $this->render('index', ['model'=>$model]);
}
}
Related
I'm building livewire components that shares 50% of public properties and almost 90% of submit function logic.
each component using this trait has its own rules according to its html-form fields. and also each component perform some custom logic after validating the data. other that that they are all the same.
<?php
namespace App\Traits;
trait ParentServiceComponent
{
public $desc = '';
public function rules()
{
return [
'desc' => 'required|max:2000'
];
}
public abstract function componentCustomLogic(array $data);
public function submit()
{
$data = $this->validate();
$performCusomLogic = $this->componentCustomLogic($data);
// save to db and show success message
}
}
here an example of two components that uses this trait.
<?php
namespace App\Http\Livewire;
use Livewire\Component;
use App\Traits\ParentServiceComponent;
class RequestService extends Component
{
public $type = '';
use ParentServiceComponent { rules as traitRules; }
public function rules()
{
return array_merge($this->traitRules, [
'type' => 'required|max:200'
]);
}
public function componentCustomLogic(array $data)
{
// do the logic of this component here
}
public function render()
{
return view('livewire.request-service');
}
}
<?php
namespace App\Http\Livewire;
use Livewire\Component;
use App\Traits\ParentServiceComponent;
class ReplyService extends Component
{
public $body = '';
use ParentServiceComponent { rules as traitRules; }
public function rules()
{
return array_merge($this->traitRules, [
'body' => 'required|max:200'
]);
}
public function componentCustomLogic(array $data)
{
// do the logic of this component here
}
public function render()
{
return view('livewire.reply-service');
}
}
so my question is: am I doing it right?
This is the ArticlesController code
<?php
namespace App\Http\Controllers;
use App\Models\Article;
use Illuminate\Http\Request;
class ArticlesController extends Controller
{
public function index()
{
$articles = Article::latest()->get();
return view('articles.index', ['articles' => $articles]);
}
public function show($id)
{
$article = Article::find($id);
return view('articles.show', ['article' => $article]);
}
}
And this is the web.php code
Route::get('/articles', 'App\Http\Controllers\ArticlesController#index');
and this is the layout.blade.php code
<li class={{Request::path() === 'articles' ? 'current_page_item' : ''}}><a href="/articles"
accesskey="4"
I tried so many different ways and it's working but in laravel 8 it's not.
public function index()
{
$articles = Article::query()->latest()->get();
return view('articles.index', ['articles' => $articles]);
}
I have a relationship in my app. A candidate can have several "candidate_trainings" and each "candidate_training" is associated with a training. I wanted to avoid making "candidate_trainings" the piviot since it's hard to delete the right values when detaching, etc. So, how can I, on my hasMany relationship get the CandidateTraining model with the data from the Training model.
Here are my relationships:
<?php
namespace App;
use App\Traits\SanitizeIds;
use App\Salary;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;
class Candidate extends Model
{
public function saveTraining($data) {
$this->candidateTrainings()->delete();
foreach(json_decode($data['training']) as $training) {
if(Training::find($training->training)->first()) {
$candidateTraining = new CandidateTraining;
$candidateTraining->description = $training->value;
$candidateTraining->training_id = $training->training;
$this->candidateTrainings()->save($candidateTraining);
}
}
}
public function candidateTrainings() {
return $this->hasMany('\App\CandidateTraining');
}
public function trainings() {
return $this->belongsToMany('\App\Training');
}
}
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Training extends Model
{
protected $fillable = ['name_french', 'name_english'];
public function candidates() {
return $this->belongsToMany('\App\Candidate');
}
public function candidateTrainings() {
return $this->hasMany('\App\CandidateTraining');
}
}
<?php
namespace App;
use Illuminate\Database\Eloquent\Relations\Pivot;
class CandidateTraining extends Pivot
{
public function candidate() {
return $this->belongsTo('\App\Candidate');
}
public function training() {
return $this->belongsTo('\App\Training');
}
}
Thank you!
For you to be able to update the data directly on the CandidateTraining model, you need to add the $fillable fields to it.
protected $fillable = ['training_id', 'description'];
Your code should work! But if you don't mind, I did a little refactoring. You can accomplish this in another way:
<?php
namespace App;
use App\Traits\SanitizeIds;
use App\Salary;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;
class Candidate extends Model
{
public function saveTraining($data)
{
// remove all relationships
$this->trainings()->detach();
// add new ones
foreach(json_decode($data['training']) as $training)
{
if(Training::find($training->training)->first())
{
$this->trainings()->attach($training->training, [
'description' => $training->value,
]);
}
}
}
public function candidateTrainings()
{
return $this->hasMany(App\CandidateTraining::class);
}
public function trainings()
{
return $this->belongsToMany(App\Training::class)
->withTimestamps()
->using(App\CandidateTraining::class)
->withPivot([
'id',
'training_id',
'description',
]);
}
}
That $training->training stuff is not readable, change it to something like $training->id if you are able to.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Training extends Model
{
protected $fillable = ['name_french', 'name_english'];
public function candidates()
{
return $this->belongsToMany(App\Candidate::class)
->withTimestamps()
->using(App\CandidateTraining::class)
->withPivot([
'id',
'training_id',
'description',
]);;
}
public function candidateTrainings()
{
return $this->hasMany(App\CandidateTraining::class);
}
}
<?php
namespace App;
use Illuminate\Database\Eloquent\Relations\Pivot;
class CandidateTraining extends Pivot
{
protected $fillable = ['training_id', 'description'];
public function candidate()
{
return $this->belongsTo(App\Candidate::class);
}
public function training()
{
return $this->belongsTo(App\Training::class);
}
}
If you want to access the pivot object from a controller:
$candidates = Candidate::with(['trainings'])->get();
foreach ($candidates as $candidate)
{
dd($candidate->pivot);
}
I have these classes:
Model:
namespace app\models;
use \yii\db\ActiveRecord;
class MyModel extends ActiveRecord {
public function rules() {
return [
[['name'], 'required'],
[['id'], 'default', 'value' => null]
];
}
}
Controller:
<?php
namespace app\controllers;
use Yii;
use yii\web\Controller;
use app\models\MyModel;
class MymodelController extends Controller{
public function actionEdit($id = null){
$model = new MyModel();
if ($model->load(Yii::$app->request->post()) && $model->validate() && $model->save()) {
Yii::$app->session->setFlash('msg', 'Model has been saved with ID ' . $model->id);
}
return $this->render('edit', [
'model' => $model
]);
}
}
View:
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
?>
<?php if(Yii::$app->session->hasFlash('msg')): ?>
<div class="alert alert-success"><?= Yii::$app->session->getFlash('msg'); ?></div>
<?php endif; ?>
<?php $form = ActiveForm::begin(); ?>
<?= Html::activeHiddenInput($model, 'id'); ?>
<?= $form->field($model, 'name') ?>
<?= Html::submitButton('Submit', ['class' => 'btn btn-primary']) ?>
<?php ActiveForm::end(); ?>
I want to use this view for editing and for inserting. Editing does not work as it is, because I am creating a new object instead of changing an existing one in the Controller. I am unsure what is the best practice here or if I am missing some already existing built in function?
Should I create my own model class and implement the logic model <-> active record in the controller
or
Should I just re query the database with the $model->id in the controller and copy all properties if needed?
you should use two actions for edit and insertion
for edit first find the model
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('edit', [
'model' => $model,
]);
}
protected function findModel($id)
{
if (($model = MyModel::findOne($id)) !== null) {
return $model;
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}
if you use CRUD for generating your controller you don't have to write these actions.
For CRUD (Create, Read/View, Update and delete) you can use gii. This power tool generate automatically all you need for an ActiveRecord, Controller with the basic action (index, view, create, update, delete, find) and related view.
In gii you first genearate model class and then generate CRUD for this class.
But the most informant things all this information are coerently related each other
see this doc is very useful and the best pratice for Yii2 is embetted in the tool
http://www.yiiframework.com/doc-2.0/guide-start-gii.html
In Your Create action:
public function actionCreate()
{
$model = new Yourmodel();
if ($model->load(Yii::$app->request->post())) {
if($model->save()){
return $this->redirect(['view']);
}
}
return $this->render('create', [
'model' => $model,
]);
}
In your Update action:
public function actionUpdate($id)
{
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post())) {
if($model->save()){
return $this->redirect(['view', 'id' => $model->id]);
}
}
return $this->render('update', [
'model' => $model,
]);
}
What I meet is almost like this:
I have a create form, and the form contains two model's attribute;
I pass them from controller to view, and add rules to validate attribute in both model;
But the form validation did not work well - a model's validation is
not work.
I have no idea about how to resolve this, thanks for help!
And I find a reference article - Complex Forms with Multiple Models, but it is TBD.
Here is my sample code.
Controller - SiteController.php:
namespace task\controllers;
use yii\web\Controller;
use task\models\Task;
use task\models\Customer;
class Task extends Controller
{
public function actionCreate()
{
$model = new Task;
$customerModel = new Customer;
// load, validate, save ...
return $this->render('create', [
'model' => $model,
'customerModel' => $customerModel
]);
}
}
Model - Task.php, Customer.php:
namespace task\models;
use yii\db\ActiveRecord;
class Task extends AcitveRecord
{
public $title, $published_at;
public function rules()
{
return [
[['title', 'published_at'], 'required'],
['published_at', 'match', 'pattern' => '/patten for time/']
];
}
}
namespace task\models;
use yii\db\ActiveRecord;
class Customer extends ActiveRecord
{
public $name;
public function rules()
{
return [
['name', 'required'],
];
}
}
View - create.php:
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
?>
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($model, 'title')->textInput() ?>
<?= $form->field($model, 'publish_at')->textInput() ?>
<?= $form->field($customerModel, 'name')->textInput() ?>
<?= Html::submitButton('submit')?>
<?php ActiveForm::end(); ?>
This can be an option. You can try it by creating an custom model, like ContactForm something like this:
<?php
namespace app\models;
use Yii;
use yii\base\Model;
/**
* CustomModel is the model behind the contact form.
*/
class CustomModel extends Model
{
public $attributeFromModel1;
public $attributeFromModel2;
/**
* #return array the validation rules.
*/
public function rules()
{
return [
// attributeFromModel1, attributeFromModel2 are required
[['attributeFromModel1', 'attributeFromModel2'], 'required'],
// ['email', 'email'],
['attributeFromModel1','YourRule']
// verifyCode needs to be entered correctly
['verifyCode', 'captcha'],
];
}
/**
* #return array customized attribute labels
*/
public function attributeLabels()
{
return [
'atttributeFromModel1' => 'Your Label',
'atttributeFromModel2' => 'Your Label ',
];
}
public function actionUpdate($id)
{
$model = $this->findModel($id);
$customerModel = Customer::findOne($id);
if (!isset($model, $customerModel)) {
throw new NotFoundHttpException("The user was not found.");
}
$model->scenario = 'update';
$customerModel->scenario = 'update';
if ($model->load(Yii::$app->request->post()) && $customerModel->load(Yii::$app->request->post())) {
$isValid = $model->validate();
$isValid = $customerModel->validate() && $isValid;
if ($isValid) {
$model->save(false);
$customerModel->save(false);
return $this->redirect(['view', 'id' => $id]);
}
}
return $this->render('update', [
'model' => $model,
'customerModel' => $customerModel,
]);
}