weird log in yii2 active record query - yii2

well I'm working with yii2 and I have , in the database schema , two tables (user and places) and they are related together via an intermediary table(junction table), i looked what is the best way to get those places that has been created by the user who is currently loged in the application. There are many ways to do it , but I use the next one:
In the User model I made a function called getPlaces():
public function getPlaces(){
return $this->hasMany(Place::className(),['id'=>'id_place'])
->viaTable('OwnerAdmins',['id_user' => 'id']);
}
and then I call this function and I pass the result to the view, the weird is that that does not show any places , Iam not getting it no error either , so when I look into the log in the database I can see the next:
SELECT * FROM ....... WHERE 0=1
well, obviously that wont display any data , but the weird there is 0=1 , i could not find what is going on with that. I have run "grep -lir "0=1" appDirectory" but it seems to be something else, does anyone know what is going on?

In Yii2, WHERE 0=1 is the result of an empty IN condition.
This simply means that your user model do not have any OwnerAdmins => the query to fetch places will have an empty IN condition.

Related

What is the least resource intensive to perform "findManyOrCreate" in Laravel eloquent?

I have an array of post codes coming from an input:
$postCodes = collect(["BK13TV", "BK14TV", "BK15TV", "BK16TV"]);
In my database I already have two of the post codes - "BK13TV", "BK16TV".
So I would like to run something like this:
$postCodeModels = PostCode::findManyOrCreate($postCodes->map($postCode) {
return ['code' => $postCode]
})
My initial approach was to load all the post codes, then diff them against the postCodes from the input like so:
PostCode::createMany($postCodes->diff(PostCode::all()->pluck('code')))
However, here it means that I am loading the entire content of post_codes table, which just seems wrong.
In the ideal case, this would return all post code models matching the passed post codes as well as would create entries for post codes that did not exist in the database.
First I need to retrieve existing postcodes:
$existingPostCodes = PostCode::whereIn('code', $postCodes)->get();
The find all the post codes in the input, that are not stored yet in database:
$newPostCodes = $postCodes->diff($existingPostCode->pluck('code'));
Finally retrieve all the new post codes as models:
$postCodeModels = PostCode::whereIn('code', $postCodes)->get();
Admittedly, this still takes three queries, but does eliminate the crazy stuff of loading an entire table worth of data.

Validation for three unique fields and soft deletes

Last year I made a laravel site with an events table where I needed three fields to be unique for any event (place, date and time). I wasn't able to set up a validation request to do this so I added an unique index for these three fields directly through phpmyadmin and catching the exception that could happen if a duplicated event was inserted.
So basically my store() method has a try/catch like this:
try {
$event = new Event;
$event->place = $request->input('place');
$event->date = $request->input('date');
$event->time = $request->input('time');
$event->save();
return view(...);
} catch (\Illuminate\Database\QueryException $e) {
// Exception if place-date-time is duplicated
if($e->getCode() === '23000') {
return view('event.create')
->withErrors("Selected date and time is not available");
}
}
Well, now I had to change the app so events could be soft deleted and I simply added the 'deleted_at' field to the unique index, thinking it would be so easy... This approach doesn't work anymore so I've been reading here and there about this problem and the only thing I get is I should do it through a validation request with unique, but honestly I just don't get the syntax for this validation rule with three fields that can't be equal while a fourth one, deleted_at, being null.
My app checks for the available places, dates and times and doesn't let the user choose any not available event but no matter how many times I've told them there's always someone who uses the browser back button and saves the event again :(
Any help will be much appreciated. Thank you!
This is not a good approach to solve the problem.
You can do follow things to solve this problem
Before insert into database get a specific row if exist from database
and store into a variable.
Then check the data is already stored into the database or not.
If data is already there create custom validation message using Message Bag Like below.
$ifExist = $event
->wherePlace(request->input('place'))
->whereDate(request->input('date'))
->whereTime(request->input('time'))
->exist();
if ($ifExist) return 'already exist';
It might help you.
#narayanshama91 have pointed the right way.
You said you would like to use the unique rule to validate the input but the problem is that last week there was a post in Laravel Blog warning users of a possible SQL Injection via the unique rule if the input is provided by the user.
I would highly advise you to NOT USE this rule in this case since you depend on users input.
The correct approach in your case would be #narayanshama91 answer.
$ifExist = $event
->wherePlace(request->input('place'))
->whereDate(request->input('date'))
->whereTime(request->input('time'))
->exist();
if ($ifExist) {
return 'already exist';
}

I can't get an e-mail of a specific contact

Here is my story:
I'm writting a script that permits to see every users in an array of group (I mean, you select 2 group, it show every users in one of these two groups). It also do some other treatment. But it's OK for this part.
Everything seems to work correcly. Except for only one user.
The idea is, I have to get the e-mail of a user, to then compare users'e-mail got in a former group, to see if this user is (or not) already listed ( in order to avoid duplicate).
The user (this one only) won't use my function. I supposed it was a group, but it really is a user.. I'm pretty sure it is an option to select ( or not) in the user's preference, but which one?
PS: here is the error quote
TypeError: Fonction getEmail introuvable dans l'objet
(TypeError: getEmail function not found in object)
And here is the code I use in order to get e-mails:
for(var i in objuser){
for(var j in objuser[i])
{
objuser[i][j]=objuser[i][j].getEmail();
}
}
Objuser is a list of User Object. First dimension (I) is the group, second dimension (j), is users of the "I" group.
PROBLEM NOT SOLVED:
the reason:
I have 2 functions that do treatments. Theses Two function need an array, that another function create (which is long to execute). My code is done in such a way, if i execute consecutively these 2 treatment functions with the same array, the second to be played use an incorrect array.
So i clone it with :
var groupsUser2 = JSON.parse(JSON.stringify(groupsUser));
but, now that i dont use anymore email adresses ( i mean String), but direct Users (i mean Object), the former code don't clone correctly:
array1 : user's array (Objects)
array2 = JSON.parse(JSON.stringify(array1))
log(array1) :[blabla1#...com,blabla2#...com,blabla3#...com, .....]
log(array2) :[{},{},{}………]
SO.... Here is the new question: Is there a simple way to copy an Object's array ?
Here is the former question: What rights configuration unallow me to use the getEmail() function for a specific contact?
I need an answer just for one of these two questions, and i'll be able to correct my problem. Any idea guys???????
never use "for x in array" its bad use of javascript on an array because the array has the "length" property which is a number and not the object your loop expects.
instead use " for (i=0;...." or forEach.
Well, I was using getEmail() function in order to compare users got in one group, to others got in another group , so that i can avoid duplicates.
I was checking with IndexOf() if the user adress were in the array of the other group's users.
I don't know why , but now it works even if i don't get the e-mail of the user. So , the problem that was happening for one user can't happen anymore.
Conclusion: Problem solved. Thx mates
I thought about a solution: try .. catch, so that the email which won't be get, will be potentially duplicated because I will not be able to find the user if already displayed or not without his e-mail, but at least the script will not crash.

Laravel Eloquent is not saving properties to database ( possibly mysql )

I'm having a strange issue.
I created a model observer for my user model. The model observer is being run at 'saving'. when I dump the object at the very end of the user model to be displayed ( this is just before it saves.. according to laravel docs ) it displays all the attributes set correctly for the object, I've even seen an error that showed the correct attributes as set and being inserted into my database table. However, after the save has been completed and I query the database, two of the fields are not saved into the table.
There is no code written by myself sitting between the point where I dumped the attributes to check that they had been set and the save operation to the database. so I have no idea what could be causing this to happen. All the names are set correctly, and like I said, the attributes show as being inserted into the database, they just never end up being saved, I receive no error messages and only two out of ten attributes aren't being saved.
In my searches I have found many posts detailing that the $fillable property should be set, or issues relating to a problem with variables being misnamed or unset, however because I already have the specific attributes not being saved specified in the $fillable array, on top of the fact that they print out exactly as expected pre save, I don't believe those issues are related to the problem I am experiencing.
to save I'm calling:
User::create(Input::all());
and then the observer that handles the data looks like this:
class UserObserver {
# a common key between the city and state tables, helps to identify correct city
$statefp = State::where('id',$user->state_id)->pluck('statefp');
# trailing zeros is a function that takes the first parameter and adds zeros to make sure
# that in this case for example, the dates will be two characters with a trailing zero,
# based on the number specified in the second parameter
$user->birth_date = $user->year.'-'.$user->trailingZeros( $user->month, 2 ).'-'.$user->trailingZeros( $user->day, 2 );
if(empty($user->city)){
$user->city_id = $user->defaultCity;
}
$user->city_id = City::where( 'statefp', $statefp )->where('name', ucfirst($user->city_id))->pluck('id');
# if the user input zip code is different then suggested zip code then find location data
# on the input zip code input by the user from the geocodes table
if( $user->zip !== $user->defaultZip ){
$latlon = Geocode::where('zip', $user->zip)->first();
$user->latitude = $latlon['latitude'];
$user->longitude = $latlon['longitude'];
}
unset($user->day);
unset($user->month);
unset($user->year);
unset($user->defaultZip);
unset($user->defaultCity);
}
that is the code for the two values that aren't being set, when I run
dd($user);
all the variables are set correctly, and show up in the mysql insert attempt screen with correct values, but they do not persist past that point.. it seems to me that possibly mysql is rejecting the values for the city_id and the birth_date. However, I cannot understand why, or whether it is a problem with Laravel or mysql.
since I was calling
User::create();
I figured I'd try to have my observer listen to:
creating();
I'm not sure why it only effected the date and city variables, but changing the function to listen at creating() instead of saving() seems to have solved my problem.

Laravel Eloquent how to limit access to logged in user only

I have a small app where users create things that are assigned to them.
There are multiple users but all the things are in the same table.
I show the things belonging to a user by retrieving all the things with that user's id but nothing would prevent a user to see another user's things by manually typing the thing's ID in the URL.
Also when a user wants to create a new thing, I have a validation rule set to unique but obviously if someone else has a thing with the same name, that's not going to work.
Is there a way in my Eloquent Model to specify that all interactions should only be allowed for things belonging to the logged in user?
This would mean that when a user tries to go to /thing/edit and that he doesn't own that thing he would get an error message.
The best way to do this would be to check that a "thing" belongs to a user in the controller for the "thing".
For example, in the controller, you could do this:
// Assumes that the controller receives $thing_id from the route.
$thing = Things::find($thing_id); // Or how ever you retrieve the requested thing.
// Assumes that you have a 'user_id' column in your "things" table.
if( $thing->user_id == Auth::user()->id ) {
//Thing belongs to the user, display thing.
} else {
// Thing does not belong to the current user, display error.
}
The same could also be accomplished using relational tables.
// Get the thing based on current user, and a thing id
// from somewhere, possibly passed through route.
// This assumes that the controller receives $thing_id from the route.
$thing = Users::find(Auth::user()->id)->things()->where('id', '=', $thing_id)->first();
if( $thing ) {
// Display Thing
} else {
// Display access denied error.
}
The 3rd Option:
// Same as the second option, but with firstOrFail().
$thing = Users::find(Auth::user()->id)->things()->where('id', '=', $thing_id)->firstOrFail();
// No if statement is needed, as the app will throw a 404 error
// (or exception if errors are on)
Correct me if I am wrong, I am still a novice with laravel myself. But I believe this is what you are looking to do. I can't help all that much more without seeing the code for your "thing", the "thing" route, or the "thing" controller or how your "thing" model is setup using eloquent (if you use eloquent).
I think the functionality you're looking for can be achieved using Authority (this package is based off of the rails CanCan gem by Ryan Bates): https://github.com/machuga/authority-l4.
First, you'll need to define your authority rules (see the examples in the docs) and then you can add filters to specific routes that have an id in them (edit, show, destroy) and inside the filter you can check your authority permissions to determine if the current user should be able to access the resource in question.