Lets start with whereHas.. That checks that all Attribute MUST have values.variantswith that product ID.. So it ensures we get the right rows.
Secondly the with.. This eager loads the values.variantsfor each Attribute.. This wouldnt limit the query in any way, but just preloads the relationship.
The queries look kinda the same but has two similar but different objectives.
If you dont query get it, we can try playing with a real world scenario.
In a room are 5 men. 3 of them have a wife
Now you call in all men who have a wife (whereHas). So in comes 3 men.. Just the men
Then you ask each man to go bring their wife.. So each of them go back to the other room to get their wife, one at the time.. Not very efficient.
So instead you call in all men with their wives (with).. Now in comes all 5 men, but 2 of them dont have a wife (but you did just say ALL men). So again. not efficient
Last you call in all men who have a wife (whereHas) and ask them to bring their wife (with).. And you get 3 men with 3 wives in one go :)