Check if Ids exist in database before attach - php

I select an array with categories via json_decode and attach them to the article.
public static function setArticleCategory(Request $request) {
$article = Article::where('id', $request->article_id)->first();
if(!$article){
return response()->json([
'message' => 'Article not found'
], 404);
}
$categories_ids = json_decode($request->categories_ids);
$article->categories()->attach($categories_ids);
return response()->json([
'message' => $categories_ids
], 200);
}
The question is, how can I check in categories_ids, for example, if one of the selected categories does not exist in the array?

The fastest way would be to use count()
$categories_ids = json_decode($request->categories_ids);
if (count($categories_ids) != Category::whereKey($categories_ids)->count()) {
//return error.
}
It would not tell you wich one is the non existing category, but it will trigger an error if at least one of them doesnt exist.
Edit (comment)*
if you want to ignore the non existing IDs, use this
$categories_ids = json_decode($request->categories_ids);
$categoryPK = (new Category())->getKeyName();
$categories_ids = Category::whereKey($categories_ids)->pluck($categoryPK)->toArray();
It will return an array of ids that exists in the database based on the input (you need to check for empty array after that).

Related

Eloquent query returns wrong data when when using union

I have in my code a union between public data and user specific data. What I want to achieve is that if there is no user logged in, I return data which public is true. In case that I have a user, I make another query where user_id is the logged in user. Everything works until I want to get a specific data id of a user that I shouldn't be allowed.
For example I have the data:
[
'id' => 1,
'user_id' => 1,
'public' => true,
],
[
'id' => 2,
'user_id' => 1,
'public' => false,
],
My current code:
public function getQuery() : Builder
{
$publicData = $this->model->where('public', true);
// $this->user is passed thought another method which is $request->user() result.
if (!isset($this->user)) {
return $publicData;
}
if ($this->user->isAdmin()) {
return $this->model->newQuery();
}
return $this->model
->where('user_id', $this->user->id)
->union($publicData);
}
Now we assume that $this->user->id is 10 and I try to fetch data that I am not allowed by id.
$data = $this->getQuery()
->where('id', 2)
->first();
In this situation, always the first public data, which in this case is id 1 will be returned and I expect to receive null.
I am not sure how to find a solution for this and I am not sure what am I missing. Currently I use Laravel 6
Potential problem in your code, it is using one query for union and result query.
You can try check this:
public function getQuery() : Builder
{
// HERE ADDED newQuery
$publicData = $this->model->newQuery()->where('public', true);
// $this->user is passed thought another method which is $request->user() result.
if (!isset($this->user)) {
return $publicData;
}
if ($this->user->isAdmin()) {
return $this->model->newQuery();
}
return $this->model
->where('user_id', $this->user->id)
->union($publicData);
}
But you recommend simplify your query, without using union, because union is unnecessary here, to example:
public function getQuery() : Builder
{
$query = $this->model->newQuery();
if ($this->user->isAdmin()) {
return $query;
}
return $query->where(function ($builder) {
$builder->where('public', true);
if (isset($this->user)) {
$builder->orWhere('user_id', $this->user->id);
}
});
}
In Laravel you get the logged in user id by auth()->id(). It seems you are trying to filter the results by the eloquents’ attached users, which will obviously return true for every row.

apiResourece does not return values on custom method

I am using laravel apiResources for an api call, the problem is that i created a custom method named closePeriod, that when i try to fetch a period it return always
No query results for model [App\Entities\Accounting\Period]
this is the method, i try to get the Period with Period::findOrFail($id), i have checked and $id has a value, and it exists in the database
Route::get('periods/close/{id}', 'PeriodController#closePeriod');
public function closePeriod($id) {
if($period = Period::findOrFail($id)) {
//do something
return response()->json([
'error' => false,
'data' => $period
]);
}
return response()->json([
'error' => true,
'message' => 'The period was not found, please reaload and try again'
]);
}
I have noticed that the api methods does work fine, i get the periods in index, show and can create and edit a period so i think the problem is in the custom method? why?

Using callable params to modify a query

I am using craftable to generate an admin panel for my app.
I have an Organisation model that belongs to an Organisation Type model.
In the index listing, I want to be able to display the Organisation Type name rather than the _id. To do this, I have to modify this query, to eager load the relationship using the 'with' method.
The method signature for the listing is:
public static function processRequestAndGet($request, $columns = array(), $searchIn = null, $modifyQuery = null, $locale = null)
and the index method is:
$data = AdminListing::create(Organisation::class)->processRequestAndGet(
// pass the request with params
$request,
// set columns to query
['id', 'organisation_type_id', 'name', 'active'],
// set columns to searchIn
['id', 'name']
);
if ($request->ajax()) {
return ['data' => $data];
}
return view('admin.organisation.index', ['data' => $data]);
Craftable, provides a modifyQuery method to, but i'm not sure how to use it:
public function index(IndexMovie $request)
{
$data = AdminListing::create(Movie::class)
->modifyQuery(function($query) use ($request){
if ($request->has('author_id')) {
$query->where('author_id', $request->author_id);
}
})
->get();
Can someone help me use the callback to modify the query so that I can include the related table data?
Okay so I've came across the exact same problem and I'd like to share my way of doing it.
craftable uses a processRequestAndGet function that takes as a 4th parametre the intended callable query. So when you need to use that parametre rather than trying to access the modifyQuery function directly.
$data_r = AdminListing::create(Organisation::class)
->processRequestAndGet(
// pass the request with params
$request,
// set columns to query
['id', 'organisation_type_id', 'name', 'active'],
// set columns to searchIn
['id', 'name'],
function($query) use ($request){
if ($request->has('author_id')) {
$query->where('author_id', $request->author_id);
}
}
);
You were almost right, just pass the intended callback inside your processRequestAndGet and voilĂ .

Laravel Eloquent $model->save() not saving but no error

When updating my Post model, I run:
$post->title = request('title');
$post->body = request('body');
$post->save();
This does not update my post. But it should according to the Laravel docs on updating Eloquent models. Why is my model not being updated?
I get no errors.
The post does not get updated in the db.
Besides not being updated in the db, nothing else seems odd. No errors. Behavior as normal.
Result of running this test to see if save succeeded was true.
This Laravel thread was no help
Post model:
class Post extends Model
{
protected $fillable = [
'type',
'title',
'body',
'user_id',
];
....
}
Post controller:
public function store($id)
{
$post = Post::findOrFail($id);
// Request validation
if ($post->type == 1) {
// Post type has title
$this->validate(request(), [
'title' => 'required|min:15',
'body' => 'required|min:19',
]);
$post->title = request('title');
$post->body = request('body');
} else {
$this->validate(request(), [
'body' => 'required|min:19',
]);
$post->body = request('body');
}
$post->save();
return redirect('/');
}
Bonus info
Running dd($post->save()) returns true.
Running
$post->save();
$fetchedPost = Post::find($post->id);
dd($fetchedPost);
shows me that $fetchedPost is the same post as before without the updated data.
Check your database table if the 'id' column is in uppercase 'ID'. Changing it to lower case allowed my save() method to work.
I had the same and turned out to be because I was filtering the output columns without the primary key.
$rows = MyModel::where('...')->select('col2', 'col3')->get();
foreach($rows as $row){
$rows->viewed = 1;
$rows->save();
}
Fixed with
$rows = MyModel::where('...')->select('primary_key', 'col2', 'col3')->get();
Makes perfect sense on review, without the primary key available the update command will be on Null.
I had the same problem and changing the way I fetch the model solved it!
Was not saving even though everything was supposedly working just as you have mentioned:
$user = User::find($id)->first();
This is working:
$user = User::find($id);
You have to make sure that the instance that you are calling save() on has the attribute id
Since Laravel 5.5 laravel have change some validation mechanism I guess you need to try this way.
public function store(Request $request, $id)
{
$post = Post::findOrFail($id);
$validatedData = [];
// Request validation
if ($post->type == 1) {
// Post type has title
$validatedData = $request->validate([
'title' => 'required|min:15',
'body' => 'required|min:19',
]);
} else {
$validatedData = $request->validate([
'body' => 'required|min:19',
]);
}
$post->update($validatedData);
return redirect('/');
}
Running dd() inside a DB::transaction will cause a rollback, and the data in database will not change.
The reason being, that transaction will only save the changes to the database at the very end. Ergo, the act of running "dump and die" will naturally cause the script to cease and no therefore no database changes.
Check your table if primary key is not id ("column name should be in small letters only") if you have set column name with different key then put code in your Model like this
protected $primaryKey = 'Id';
So this might be one of the possible solution in your case also if your column name contains capital letters.
Yes this worked for me fine,
You should have column names in small letter,
If you don't have then mention it in the model file, mainly for primaryKey by which your model will try to access database.
For use save () method to update or delete if the database has a primary key other than "id". need to declare the attribute primaryKey = "" in the model, it will work
Try this
public function store($id,Request $request)
{
$post = Post::findOrFail($id);
// Request validation
if ($post->type == 1) {
// Post type has title
$request->validate([
'title' => 'required|min:15',
'body' => 'required|min:19',
]);
$post->update([
'title' => request('title');
'body' => request('body');
]);
} else {
$request->validate([
'body' => 'required|min:19',
]);
$post->update([
'body' => request('body');
]);
}
return redirect('/');
}
In my experience, if you select an Eloquent model from the db and the primary_key column is not part of the fetched columns, your $model->save() will return true but nothing is persisted to the database.
So, instead of doing \App\Users::where(...)->first(['email']), rather do \App\Users::where(...)->first(['id','email']), where id is the primary_key defined on the target table.
If the (sometimes micro-optimization) achieved by retrieving only a few columns is not really of importance to you, you can just fetch all columns by doing \App\Users::where(...)->first(), in which case you do not need to bother about the name of the primary_key column since all the columns will be fetched.
If you using transactions.
Do not forget call DB::commit();
It must look like this:
try{
DB::beginTransaction();
// Model changes
$model->save();
DB::commit();
}catch (\PDOException $e) {
DB::rollBack();
}
I have the same issue although there are try / catch block in controller#action() but there were no response, it just stops at $model->save(); there is no log entry either in apache error.log or laravel.log. I have just wrapped the save() with try / cactch as follows, that helped me to figure out the issue
try{
$model->save();
}
catch (\PDOException $e) {
echo $e->getMessage();
}
I have been experiencing the same issue and found a workaround. I found that I was unable to save() my model within a function called {{ generateUrl() }} on my home.blade.php template. What worked was moving the save() call to the controller that returns the home.blade.php template. (IE, save()ing before the view is returned, then only performing read operations within {{ generateUrl() }}.)
I was (and am) generating a state to put in a URL on page load:
<!--views/home.blade.php-->
Add Character
Below is what did not work.
// Providers/EveAuth.php
function generateUrl()
{
$authedUser = auth()->user();
if (!$authedUser) {
return "#";
}
$user = User::find($authedUser->id);
$user->state = str_random(16);
$user->save();
$baseUrl = 'https://login.eveonline.com/oauth/authorize?state=';
return $baseUrl . $user->state;
}
This was able to find() the User from the database, but it was unable to save() it back. No errors were produced. The function appeared to work properly... until I tried to read the User's state later, and found that it did not match the state in the URL.
Here is what did work.
Instead of trying to save() my User as the page was being assembled, I generated the state, save()d it, then rendered the page:
// routes/web.php
Route::get('/', 'HomeController#index');
Landing at the root directory sends you to the index() function of HomeController.php:
// Controllers/HomeController.php
public function index()
{
$authedUser = auth()->user();
if ($authedUser) {
$user = User::find($authedUser->id);
$user->state = str_random(16);
$user->save();
}
return view('home');
}
Then, when generating the URL, I did not have to save() the User, only read from it:
// Providers/EveAuth.php
function generateUrl()
{
$authedUser = auth()->user();
$user = User::find($authedUser->id);
$baseUrl = 'https://login.eveonline.com/oauth/authorize?state=';
return $baseUrl . $user->state;
}
This worked! The only difference (as far as I see) is that I'm save()ing the model before page assembly begins, as opposed to during page assembly.

Apply certain condition or validation to restrict operation in Laravel Models

This is a category table I am using in my project using Laravel.
I have checks applied in the view files, for the category parent selection dropdown, so that the category itself and it's child's will not appear in the dropdown.
But form input fields value can be easily overridden using dev console.
Is there a way in models so that if parent id is equal to the category id itself or parent id is the child of current category then it will stop execution.
I have recently started laravel, a month ago, and still learning and building, so help here will be appreciated.
I was able to resolve the issue by overriding the update method in model -
Controller update method -
public function update(Request $request, $id)
{
$this->validate($request,
['name' => 'required',]);
$data = [];
$data = ['name' => Input::get('name'),
'parent' => !empty(Input::get('parent')) ? Posts_categories::find(Input::get('parent'))->id : NULL,];
$category = Posts_categories::find($id);
if(is_null($category))
{
Session::flash('flash-message', 'Category type with the given id does not exist.');
Session::flash('alert-class', 'alert-warning');
return redirect()->route('admin.post.category.index');
}
if($category->update($data)) {
Session::flash('flash-message', 'Category succesfully updated.');
Session::flash('alert-class', 'alert-success');
}
return redirect()->route('admin.post.category.index');
}
Model update method -
public function update(array $attributes = [], array $options = [])
{
$parent = SELF::find($attributes['parent']);
if($this->id == $parent->id || $this->id == $parent->parent)
{
Session::flash('flash-message', 'Invalid parent selection for category.');
Session::flash('alert-class', 'alert-warning');
return 0;
}
return parent::update($attributes, $options); // TODO: Change the autogenerated stub
}

Categories