Save data with Belong to many cakehp 3.x - has-and-belongs-to-many

I have two tables articles and tags and third association table articles_tagsnow i want to save data in articles_tags when create new article and user select multiple tags from drop down my controller code is below
public function add()
{
$article = $this->Articles->newEntity();
if ($this->request->is('post')) {
$article = $this->Articles->patchEntity($article, $this->request->data, [
'associated' => ['Tags']
]);
if ($this->Articles->save($article)) {
$this->Flash->success(__('The article has been saved.'));
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error(__('The article could not be saved. Please, try again.'));
}
}
$tags = $this->Articles->Tags->find('list',[
'keyField' => 'id',
'valueField' => 'name'
]);
$this->set(compact('article','tags'));
$this->set('_serialize', ['article','tags']);
}
and my view code is below
<?php
$this->Form->create($article);
echo $this->Form->input('title');
echo $this->Form->input('status');
echo $this->Form->select('tags.tag_id',
$tags,
['empty' => '(choose one)', 'multiple' => true,]
);
echo $this->Form->button(__('Submit'));
echo $this->Form->end();
?>
and my article and tag model code is below
public function initialize(array $config)
{
parent::initialize($config);
$this->table('articles');
$this->displayField('title');
$this->primaryKey('id');
$this->addBehavior('Timestamp');
$this->belongsToMany('Tags', [
'foreignKey' => 'article_id',
'targetForeignKey' => 'tag_id',
'joinTable' => 'articles_tags'
]);
}
public function initialize(array $config)
{
parent::initialize($config);
$this->table('tags');
$this->displayField('name');
$this->primaryKey('id');
$this->addBehavior('Timestamp');
$this->belongsToMany('Articles', [
'foreignKey' => 'tag_id',
'targetForeignKey' => 'article_id',
'joinTable' => 'articles_tags'
]);
}
but when i saving data than data save only in articles table only related tag id and article id not saving in article_tags table don't know what is the problem please help me
Thanks

correct your view code
<?php
$this->Form->create($article);
echo $this->Form->input('title');
echo $this->Form->input('status');
echo $this->Form->select('tags._ids',
$tags,
['empty' => '(choose one)', 'multiple' => true,]
);
echo $this->Form->button(__('Submit'));
echo $this->Form->end();
?>

Related

Laravel Many to one in Resource

I use laravel 8 & have 3 table:
Products, ProductPrice & ProductsPublisher:
this is my Products model for this relationship:
public function lastPrice(){
return $this->hasMany(ProductPrice::class)->where('status','active')->orderBy('created_at','DESC')->distinct('publisher_id');
}
and this is my productsPrice model for publisher relationship:
public function getPublisher(){
return $this->belongsTo(ProductsPublisher::class,'publisher_id');
}
now, i want to use laravel resource for my api, i wrote products resource:
public function toArray($request)
{
return [
'id' => $this->id,
'price' => lastPrice::make($this->lastPrice),
'status' => $this->status,
'slug' => $this->slug,
'title' => $this->title,
'description' => $this->description,
'txt' => $this->txt,
'lang' => $this->lang,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
but in lastPrice resource, when i wrote like this:
return [
'id' => $this->id,
'main_price' => $this->main_price
];
it give me this error:
Property [id] does not exist on this collection instance.
when i use this code:
return parent::toArray($request);
get response but because i need to use another relationship in my lastPirce for publishers, i cant use that code and should return separately my data.
What i should to do?
thanks
Edit 1:
this is my Controller Code:
$products = Product::where('id',$id)->where('slug',$slug)->where('status','confirm')->first();
if(!$products){
return $this->sendError('Post does not exist.');
}else{
return $this->sendResponse(new \App\Http\Resources\Products\Products($products), 'Posts fetched.');
}
and this is sendResponse & sendError:
public function sendResponse($result, $message)
{
$response = [
'success' => true,
'data' => $result,
'message' => $message,
];
return response()->json($response, 200);
}
public function sendError($error, $errorMessages = [], $code = 404)
{
$response = [
'success' => false,
'message' => $error,
];
if(!empty($errorMessages)){
$response['data'] = $errorMessages;
}
return response()->json($response, $code);
}
thanks.
Edit 2:
i change my lastPrice Resource toArray function to this and my problem solved, but i think this isn't a clean way, any better idea?
$old_data = parent::toArray($request);
$co = 0;
$new_data = [];
foreach ($old_data as $index){
$publisher_data = Cache::remember('publisher'.$index['publisher_id'], env('CACHE_TIME_LONG') , function () use ($index) {
return ProductsPublisher::where('id' , $index['publisher_id'])->first();
});
$new_data[$co]['main_prices'] = $index['main_price'];
$new_data[$co]['off_prices'] = $index['off_price'];
$new_data[$co]['publisher'] = SinglePublisher::make($publisher_data);
$new_data[$co]['created_at'] = $index['created_at'];
$co++;
}
return $new_data;

Attatch multiple values from checkox, with many-to-many relation in Laravel

I am trying to attatch book and warehouses upon creating a new book in my laravel project.
At the moment i have a checkbox for each warehouse, in my view:
#foreach($warehouses as $warehouse)
<input type="checkbox" name="checked[]" value="{{ $warehouse->id }}">
{{ $warehouse->address }}
<br/>
#endforeach
And i have the many to many relations in my book and warehouse models:
Book:
class Book extends Model
{
use HasFactory;
protected $table = 'books';
protected $fillable = [
'ISBN', 'publisher_id', 'author_id', 'year', 'title', 'price',
];
public function warehouses()
{
return $this->belongsToMany(Warehouse::class);
}
Warehouse:
class Warehouse extends Model
{
use HasFactory;
protected $table = 'warehouses';
protected $fillable = [
'name', 'address', 'phone', 'url',
];
public function books()
{
return $this->belongsToMany(Book::class);
}
When submitting the foreach in the form on my book.create view, i try to attach each checked warehouses in the same process as creating the new book:
My Create method:
public function create()
{
$authors = Author::all();
$selectedAuthor = Book::first()->author_id;
$publishers = Publisher::all();
$selectedPublisher = Book::first()->publisher_id;
$warehouses = Warehouse::all();
$selectedWarehouse = Book::first()->warehouse_id;
return view('books.create', compact(['authors', 'publishers', 'warehouses'],
['selectedAuthor', 'selectedPublisher', 'selectedWarehouse']
));
}
And my store method to store the created data:
public function store(Request $request)
{
$request->validate([
'ISBN' => 'required',
'author_id' => 'required',
'publisher_id' => 'required',
'year' => 'required',
'title' => 'required',
'price' => 'required',
]);
try {
Book::create($request->all());
$book = Book::first(); // Book::first(); saves to the first found book (id 1), needs to be fixed to the requested book.
foreach ($request->checked as $value){
$book->warehouses()->attach([$value]);
}
return redirect()->route('books.index')
->with('success','Book created successfully.');
} catch (\Illuminate\Database\QueryException $e) {
var_dump($e->errorInfo);
}
}
The problem is my
$book = Book::first();
saves to the first found book (id 1, even though i might be creating book id 43), needs to be fixed to the requested book that is currently being created.
I have updated my store method by assigning
Book::create($request->all());
To my $book variable which solved the problem, my store method now looks like this:
public function store(Request $request)
{
$request->validate([
'ISBN' => 'required',
'author_id' => 'required',
'publisher_id' => 'required',
'year' => 'required',
'title' => 'required',
'price' => 'required',
]);
try {
$book = Book::create($request->all());
foreach ($request->checked as $value){
$book->warehouses()->attach([$value]);
}
return redirect()->route('books.index')
->with('success','Book created successfully.');
} catch (\Illuminate\Database\QueryException $e) {
var_dump($e->errorInfo);
}
}
And saves the many-to-many relational data upon storing.
Thank you very much for the help Dennis :-)

Error column not found, but I did not declare the column?

I'm inserting a record to a polymorphic imageable table, however it says column thread_id not found. I have not declared this thread_id column and I don't know where it's pulling it from. Here is the code it's trying to run.
protected static function bootRecordImage()
{
if (auth()->guest()) return;
foreach (static::getMethodToRecord() as $event) {
static::$event(function ($model) use ($event) {
$body = request()->body;
preg_match_all('/<img .*?(?=src)src=\"([^\"]+)\"/si', $body, $matches);
$images = $matches[1];
if($event == 'created') {
foreach ($images as $image) {
$model->images()->create([
'user_id' => auth()->id(),
'imageable_id' => $model->id,
'imageable_type' => get_class($model),
'path' => $image
]);
}
}
if($event == 'deleting') {
foreach ($images as $image) {
$model->images()->delete([
'user_id' => auth()->id(),
'imageable_id' => $model->id,
'imageable_type' => get_class($model),
'path' => $image
]);
if (File::exists(public_path($image))) {
File::delete(public_path($image));
}
}
}
});
}
}
My store method:
public function store(Request $request, Channel $channel, Spam $spam)
{
if (!auth()->user()) {
return back()->withInput()->with('flash', 'Sorry! You must be logged in to perform this action.');
}
if (!auth()->user()->confirmed) {
return back()->withInput()->with('flash', 'Sorry! You must first confirm your email address.');
}
$this->validate($request, [
'title' => 'required',
'body' => 'required',
'channel_id' => 'required|exists:channels,id',
'g-recaptcha-response' => 'required'
// yes it's required, but it also needs to exist on the channels model, specifically on the id
]);
$response = Zttp::asFormParams()->post('https://www.google.com/recaptcha/api/siteverify', [
'secret' => config('services.recaptcha.secret'),
'response' => $request->input('g-recaptcha-response'),
'remoteip' => $_SERVER['REMOTE_ADDR']
]);
// dd($response->json());
if (! $response->json()['success']) {
throw new \Exception('Recaptcha failed');
}
$spam->detect(request('title'));
$spam->detect(request('body'));
$thread = Thread::create([
'user_id' => auth()->id(),
'channel_id' => request('channel_id'),
'title' => request('title'),
'body' => request('body'),
//'slug' => str_slug(request('title'))
]);
return redirect('/forums/' . $thread->channel->slug . '/' . $thread->slug);
}
As you can see, no where is a thread_id mentioned, yet in the error it looks like it's trying to insert into a thread_id column that I've never declared.
Thanks for reading.
I put the polymorphic relation in the model and the trait. Remove it from the Model and you're good to go.

Cakephp 3.4 - How to validated associated relation data?

For Example I have Club table and Club detail tables. Club table has many details.
And My form control is like below:
$this->Form->control('name');
$this->Form->control('club_payment_settings.0.no_of_account');
$this->Form->control('club_payment_settings.0.actual_rate');
$this->Form->control('club_payment_settings.1.no_of_account');
$this->Form->control('club_payment_settings.1.actual_rate');
$this->Form->control('club_payment_settings.2.no_of_account');
$this->Form->control('club_payment_settings.2.actual_rate');
And in controller written code as bellow:
$club = $this->Clubs->patchEntity($club, $this->request->getData(), [
'associated' => [
'ClubPaymentSettings' => [
'validate' => 'default'
],
]
]);
How can I validate in Model validationDefault()? I am trying to do as follow in my ClubsTable.php file:
public function initialize(array $config)
{
parent::initialize($config);
$this->setTable('clubs');
$this->setPrimaryKey('id');
$this->addBehavior('Timestamp');
$this->hasMany('ClubPaymentSettings', [
'foreignKey' => 'club_id',
'joinType' => 'LEFT'
]);
}
public function validationDefault(Validator $validator)
{
$validator
->integer('id')
->allowEmpty('id', 'create');
$validator
->requirePresence('name', true)
->notEmpty('name');
$settingValidator = new Validator();
$settingValidator->requirePresence('no_of_account', true);
$settingValidator->requirePresence('actual_rate', true);
$validator->addNestedMany('club_payment_settings', $settingValidator);
}

How to Use unique rules in active record yii2

I want to set the values of my table column set as unique value, how i can use to set error if in insert form, I insert the same value as data in my database?
Is it true?
public function rules()
{
return [
[['nama_barang', 'harga', 'stok', 'id_satuan'], 'required'],
[['harga', 'stok', 'id_satuan'], 'integer'],
['nama_barang', 'unique', 'targetAttribute' => ['nama_barang' => 'nama_barang']],
[['foto'], 'safe']
];
}
Remember: model, view, controller.
Model
add unique validator in your model rules like
...
[['nama_barang'], 'unique'],
...
View
Enable ajax validation in your form view
...
<?php $form = ActiveForm::begin(['enableAjaxValidation' => true]); ?>
...
Controller
Add ajax validation in your controller
Create Action
...
public function actionCreate()
{
$model = new Product();
if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
Yii::$app->response->format = Response::FORMAT_JSON;
return ActiveForm::validate($model);
}
if ($model->load(Yii::$app->request->post())) {
...
and Update Action
...
public function actionUpdate($id)
{
$model = $this->findModel($id);
if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
Yii::$app->response->format = Response::FORMAT_JSON;
return ActiveForm::validate($model);
}
if ($model->load(Yii::$app->request->post())) {
...
PS: if not present, add required classes in your controller.
use yii\web\Response;
use yii\widgets\ActiveForm;
Try this way
public function rules()
{
return [
[['nama_barang', 'harga', 'stok', 'id_satuan'], 'required'],
[['harga', 'stok', 'id_satuan'], 'integer'],
['nama_barang', 'unique', 'targetAttribute' => ['nama_barang'], 'message' => 'Username must be unique.'],
[['foto'], 'safe']
];
}
Just set unique in the rules [['name'], 'unique'],
Below is the complete function.
public function rules()
{
return [
[['name', 'description', 'comp_id'], 'required'],
[['description'], 'string'],
[['comp_id'], 'integer'],
[['name'], 'string', 'max' => 100,],
[['name'], 'unique'],
[['comp_id'], 'exist', 'skipOnError' => true, 'targetClass' => Company::className(), 'targetAttribute' => ['comp_id' => 'comp_id']],
];
}
I had a similar problem whereby when I insert a record with an existing unique field the framework remained silent returning my view back without any error.
So, the trick around this was to do a success-redirect only when $model->save() has a boolean value of true, else render back the _form.php through your view.php

Resources