How to access a variable saved in the store method? - php

I'm using nested set comments (Kalnoy package) in my project and I'm stuck at creating children comments. I created two different method for both type of comments.
Saving root comments works fine:
public function storeComments(Request $request, Post $post)
{
$comment = Comment::create(
[
'body' => request('body'),
'user_id' => auth()->id(),
'post_id' => $post->id,
]
)->saveAsRoot();
return back();
}
However children comments are still saved as root comments.
public function storeNestedComments(Request $request, Comment $comment, Post $post)
{
$comment->children()->create(
[
'body' => request('body'),
'user_id' => auth()->id(),
'parent_id' => $comment->id,
'post_id' => $post->id,
]
);
return back();
}
This $comment variable in the second method is naturally null. How can I access the comment that was saved as root?
Update: saveAsRoot() logic
public function saveAsRoot()
{
if ($this->exists && $this->isRoot()) {
return $this->save();
}
return $this->makeRoot()->save();
}

This should do the trick:
public function storeNestedComments($parent_comment_id)
{
$parent = Comment::findOrFail($parent_comment_id);
Comment::create([
'body' => request('body'),
'user_id' => auth()->id(),
'parent_id' => $parent->id,
'post_id' => $parent->post_id
], $parent);
return back();
}
I corrected the way you are retrieving the parent commend, it does the same, but better written, plus it will throw a ModelNotFoundExceptionif the comment cannot be retrieved :)

#Amaury gave me a hint :)
I changed my route to include the root comment id
Route::post('/posts/{post}/{comment}/nestedcomments', 'CommentsController#storeNestedComments');
Passed that id to the method, and associated the child id with the parent.
public function storeNestedComments($parent_comment_id)
{
$comment = Comment::where('id', $parent_comment_id)->first();
$nestedComment = Comment::create(
[
'body' => request('body'),
'user_id' => auth()->id(),
'parent_id' => $parent_comment_id,
'post_id' => $comment->post_id,
]
);
$nestedComment->parent()->associate($comment)->save();
return back();
}

Related

laravel create wont store first row of data array

I have select options in my form where it has first row when page loads and users can add more rows by AJAX and store all rows data at once.
The problem is that my first row (which is visible when page loads) does not save the data while all other added rows will be saved.
Here is a screenshot of my select options when page loads:
And here it is when user adds new rows:
Here is my sample data that those rows have sent to controller (screenshot #2):
array:2 [▼
0 => array:3 [▼
"name" => "ali"
"username" => "alireza"
"action" => "delete"
]
1 => array:3 [▼
"name" => "eraty"
"username" => "aery"
"action" => "optional"
]
]
As I explained in my screenshots, object 0 data will not be store in the database, not sure why.
Here is my controller:
public function update(Request $request, $id)
{
$this->validate($request, [
'note' => 'required|string',
]);
$post = Post::where('id', $id)
->where('user_id', Auth::id())
->first();
$post->user_id = Auth::id();
$post->note = $request->input('note');
if ($post->save()) {
// social media (add new)
$socialHeir = $request->input('social_media_heir');
$socialAction = $request->input('social_media_action');
$socialNames = $request->input('social_media_name');
$socialUsernames = $request->input('social_media_username');
if (!empty($socialNames)) {
$result_array = [];
foreach ($socialNames as $key => $val) {
$result_array[$key] = [
'name' => $socialNames[$key],
'username' => $socialUsernames[$key],
'action' => $socialAction[$key]
];
}
// dd($result_array); <-- result of this line I shared above (after screenshot 2)
foreach ($result_array as $key => $merge) {
if (!empty($key) && !empty($merge)) {
SocialMedia::create([
'owner_id' => Auth::id(),
'heir_id' => $socialHeir,
'post_id' => $post->id,
'name' => $merge['name'],
'username' => $merge['username'],
'what_to_do' => $merge['action'],
]);
}
}
}
}
return redirect()->route('posts.index');
}
Update
SocialMedia model:
protected $fillable = [
'owner_id',
'heir_id',
'post_id',
'name',
'username',
'what_to_do',
];
public function post()
{
return $this->belongsTo(Post::class);
}
Post model
protected $fillable = [
'user_id',
'note',
];
public function socialMedias()
{
return $this->hasMany(SocialMedia::class);
}
Solved
The issue was in $key in my foreach after removing it, it saves all rows now.
foreach($result_array as $merge) {
if(!empty($merge)) {
SocialMedia::create([
'owner_id' => Auth::id(),
'heir_id' => $socialHeir,
'post_id' => $will->id,
'name' => $merge['name'],
'username' => $merge['username'],
'what_to_do' => $merge['action'],
]);
}
}

Laravel API Resource not returning correct value with condition

I am new to Laravel API, I want to return a book where recommended === 1
In my resources, I have this
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'about' => $this->about,
'content' => $this->content,
// 'image' => asset('/storage/'.$this->image),
'image' => $this->image_url,
// 'recommended' => $this->recommended,
'recommended' => $this->when($this->recommended === 1, $this->recommended),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
'author' => $this->author,
];
I want to return books when recommended === 1
My table is Like this
public function up()
{
Schema::create('books', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->text('about');
$table->string('image');
$table->string('image_url');
$table->string('epub_url');
$table->integer('author_id');
$table->string('publisher');
$table->year('year');
$table->boolean('recommended')->default(0);
$table->timestamps();
});
I was able to achieve the same thing on web using this
public function index()
{
$data = array();
$data['recommends'] = Book::where('recommended', 1)->take(10)->get();
$data['latests'] = Book::orderBy('created_at', 'desc')->take(10)->get();
return view('welcome', compact("data"));
}
But I don't know how to replicate the same using Laravel API.
UPDATE
I was able to achieve the same thing on web using this
public function index()
{
$data = array();
$data['recommends'] = Book::where('recommended', 1)->take(10)->get();
$data['latests'] = Book::orderBy('created_at', 'desc')->take(10)->get();
return view('welcome', compact("data"));
}
But I don't know how to replicate the same using Laravel API.
Normally I will get all Books or Post like this using API Resource
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'about' => $this->about,
'content' => $this->content,
'image' => $this->image_url,
'recommended' => $this->recommended,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
'author' => $this->author,
];
and call it like this in my controller
public function indexapi()
{
return BookResource::collection(Book::with('author')->Paginate(16));
}
But there some cases recommended is == 1 and some recommended == 0, in this case, I want to return data only when recommended == 1
I know my question is quite confusing
Thanks.
Thanks.
If I get it right, you want to filter & get only the books with ( recommended attribute == 1 ). If thats the case you shouldn't do it in your Collection file. You should do this filtering process in your Controller before passing any data to Collection.
Here is some code example from one of my project.
In ProductController.php FILE
public function index()
{
return new ProductCollection( Product::where('recommended','1')->get() );
}
As you can see , I'm filtering the products to get only the recommended ones. Then I'm sending this filtered data to the ProductCollection. This way The collection will only return the data I want.
In ProductCollection.php FILE
public function toArray($request)
{
return [
'data' => $this->collection->map( function($data) {
return [
'id' => (integer) $data->id,
'name' => $data->name,
'category_id' => $data->category_id,
'brand_id' => $data->brand_id,
'photos' => json_decode($data->photos),
'gtin' => $data->gtin
];
})
];
}
I don't have to make any changes in Collection. Because in this way , Collection should do the job for every data it gets.

Create new record using 2amigos SelectizeDropDownList in Yii2

I am trying to implement the 2amigos SelectizeDropDownList widget in a form to add new values to a table directly within the dropdown.
I am using the model Book and the Model Author so basically want to be able to add a new author in the book form.
This is the book controller at the update function:
public function actionUpdate($id) {
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['index']);
} else {
return $this->render('update', [
'model' => $model,
'categories' => BookCategory::find()->active()->all(),
'publishers' => Publisher::find()->all(),
'copirights' => Copiright::find()->all(),
'authors' => Author::find()->all(),
]);
}
}
This is the form:
<?=
$form->field($model, 'author_id')->widget(SelectizeDropDownList::className(), [
// calls an action that returns a JSON object with matched
// tags
'loadUrl' => ['author/list'],
'value' => $authors,
'items' => \yii\helpers\ArrayHelper::map(\common\models\author::find()->orderBy('name')->asArray()->all(), 'id', 'name'),
'options' => [
'class' => 'form-control',
'id' => 'id'
],
'clientOptions' => [
'valueField' => 'id',
'labelField' => 'name',
'searchField' => ['name'],
'autosearch' => ['on'],
'create' => true,
'maxItems' => 1,
],
])
?>
And this is the function author controller:
public function actionList($query) {
$models = Author::findAllByName($query);
$items = [];
foreach ($models as $model) {
$items[] = ['id' => $model->id, 'name' => $model->name];
}
Yii::$app->response->format = \Yii::$app->response->format = 'json';
return $items;
}
The form works fine to load, filter, search and add new items.
But it is not inserting the new typed attribute in the author table.
Do I need to add something in the book controller?
How can I check if it is a new value or a change of an existing author?
Thanks a lot
I made it work with the following code, not sure the most elegant because i am checking the if the author_id is a number or a string.
In my case the author won't be a number anyway.
public function actionUpdate($id) {
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post())) {
$x = Yii::$app->request->post('Book');
$new_author = $x['author_id'];
if (!is_numeric($new_author)) {
$author = new Author();
$author->name = $new_author;
$author->save();
$model->author_id = $author->id;
}
if ($model->save()) {
return $this->redirect(['index']);
}
} else {
return $this->render('update', [
'model' => $model,
'categories' => BookCategory::find()->active()->all(),
'publishers' => Publisher::find()->all(),
'copirights' => Copiright::find()->all(),
'authors' => Author::find()->all(),
]);
}
}

Cake 3 saving belongsToMany relation crashes

I currently have an belongsToMany relationship between two Table, Skus and Medias. I named the join table skus_images though.
I'm here trying to save only ids, not inserting new data in an HABTM way.
I have in my form :
echo $this->Form->input('images._ids', ['options' => $images, 'multiple' => 'checkbox']);
And everything is working fine there, I'm correctly getting my Medias listed.
But whenever I try to submit the form, I get this :
Error: Call to a member function get() on a non-object
File /home/weshguillaume/AndyToGaby/vendor/cakephp/cakephp/src/ORM/Association/BelongsToMany.php
Line: 874
I've defined my relationship as such in SkusTable :
$this->belongsToMany('Images', [
'className' => 'Media.Medias',
'joinTable' => 'skus_images',
'targetForeignKey' => 'image_id'
]);
The context doesn't give any insights, neither does the stack trace as it's both (almost) empty. Thanks :)
EDIT:
Controller add method:
public function add($product_id)
{
$skus = $this->Skus->newEntity();
if ($this->request->is('post')) {
$skus = $this->Skus->patchEntity($skus, $this->request->data(), [
'associated' => [
'Attributes'
]
]);
if ($this->Skus->save($skus)) {
$this->Flash->success('The skus has been saved.');
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error('The skus could not be saved. Please, try again.');
}
}
$attributes = $this->Skus->Attributes->find('list');
$images = $this->Skus->Products->getMedias('list', $product_id, 'photo');
$this->set(compact('skus', 'products', 'attributes', 'images', 'product_id'));
$this->set('_serialize', ['skus']);
}
Controller posted data:
[
'product_id' => '65',
'attributes' => [
'_ids' => ''
],
'reference' => '',
'quantity' => '420',
'is_default' => '0',
'images' => [
'_ids' => [
(int) 0 => '90'
]
]
]
Forgot to add the name of the association in the patchEntity associated option. Still shouldn't throw a fatal error so I created a github ticket.

Unique key violation upon edit

I am trying to update a blog post but I am getting unique key error from database part then I went without using model and directly accessing ORM but then again no success.
This is my routes spesific to edit
Route::get('/getedit/{slug}', array('as' => 'getedit', 'uses' => 'AdminController#getEdit'))->before('auth');
Route::post('/postedit', array('as' => 'postedit', 'uses' => 'AdminController#postEdit'))->before('auth');
Controller
public function getEdit($slug)
{
$article = Post::where('slug', '=' , $slug)
->firstOrFail();
return View::make('admin.edit', array(
'title' => $article->title,
'mainarticle' => $article->article,
'slug' => $article->slug,
'category' => $article->category
));
}
// Updates articles to database
public function postEdit()
{
$rules = [
'title' => 'required',
'article' => 'required',
'slug' => 'required|unique:posts,slug,9',
'category' => 'required'
];
$input = Input::all();
$validator = Validator::make($input, $rules);
if ($validator->fails()) {
return Redirect::route('getedit')
->withErrors($validator);
// withInput not defined
}
else
{
$slug = $input['slug'];
/*$affectedRows = Post::where('slug', '=', $slug)->update([
'title' => $input['title'],
'article' => $input['article'],
'slug' => $input['slug'],
'category' => $input['category']
]);*/
/*$affectedRows = Post::where('slug', '=', $slug)->firstOrFail();
$affectedRows->title = $input['title'];
$affectedRows->article = $input['article'];
$affectedRows->slug = $input['slug'];
$affectedRows->category = $input['category'];
$affectedRows->save();*/
$post = DB::table('posts')->where('slug', '=', $slug)->update([
'title' => $input['title'],
'article' => $input['article'],
'slug' => $input['slug'],
'category' => $input['category']
]);
if ($post) {
return Redirect::route('dashboard')
->with('flash_message','Article Successfully Inserted');
}
else
{
return Redirect::route('dashboard')
->with('flash_message','Error updating data');
}
}
}
My model is just creating object of database (I am accidentally following fat controller and thin model approach as I am just trying the framework).
I have tried using Post::find(1)->update($data); method but that is returning unique violation and my current approach is just executing else statement which is triggered upon update failure.
Note: I am new to Laravel and trying this for the first time.
When you update a post, you'd rather send a POST (or better PATCH/PUT- http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html) request to given resource.
That said, you would include edited row key in the url, and change your method to something like this:
// route
Route::post('/postedit/{id}', array('as' => 'postedit', 'uses' => 'AdminController#postEdit'))
->before('auth');
// controller
public function postEdit($id)
{
// if no posts with $id found, throws exception - catch it and eg. show 404
$post = Post::findOrFail($id);
$rules = [
'title' => 'required',
'article' => 'required',
'slug' => 'required|unique:posts,slug,'.$id, // to ignore this row in unique check
'category' => 'required'
];
// validate
$post->fill($input)->save(); // fill() in order to use mass-assignement check
// alternatively you can just update:
// $post->update($input);
// but then make sure $input has only elements corresponding to the table columns
Additionally, read about route grouping, so you don't need to add before('auth') to those routes separately.
You should check your database table indexes. You should make sure that only slug has unique index.
I see that you are checking unique for slug but you hardcoded 9 in the rule:
'slug' => 'required|unique:posts,slug,9',
It should be:
'slug' => 'required|unique:posts,slug,'.$id,
where $id id of post you try to edit.
You should include such id in your form as hidden element and not search records with slug that you have because it seems you can edit your slug and you may edit the wrong record or edit nothing.

Categories