Show reaction count on Laravel - laravel

I'm using this package as a voting system, I use Laravel 7, I avoid pasting the various codes because you can find all the documentation here: https://github.com/qirolab/laravel-reactions
I was testing the whole thing, but can't show a certain reaction count? I mean, $article->reactionSummary() shows an array of all reactions, but I'd like to show the "Like" reaction count.
https://github.com/qirolab/laravel-reactions#reaction-summery-on-model

If you want to retrieve a value from your reactionSummary you can do:
$reactionSummary = $article->reaction_summary->toArray();
$likesCount = $reactionSummary['like'];
as the docs says: Reaction summary on model
$article->reactionSummary();
$article->reaction_summary;
// example
$article->reaction_summary->toArray();
// output
/*
[
"like" => 5,
"dislike" => 2,
"clap" => 4,
"hooray" => 1
]
*/
The $article->reaction_summary->toArray(); will gives you an array containing a like key and so on.
EDIT 1:
If the key like does not exists you can do something like this:
$reactionSummary = $article->reaction_summary->toArray();
$likesCount = 0;
if (array_key_exists('like', $reactionSummary)) {
$likesCount = $reactionSummary['like'];
}

Related

Laravel 8 - Calling multiple factory states at once

Pre Laravel 8 there seems to be an option to line up different states at once
$users = factory(App\User::class, 5)->states('premium', 'delinquent')->make();
(edit: I now realise that this was to apply 2 different states into the same factory record)
whereas I can't seem to find the equivalent of this in Laravel 8, does anybody know how?
Essentially I am trying to call one chain to create multiple records with different states for example:
User::factory()->verified()->unverified()->banned()->create()
Result: 3 different users one verified one unverified one banned
It can be achieved with below code, it just seems a little verbose
User::factory()->verified()->create();
User::factory()->unverified()->create();
User::factory()->banned()->create();
1. Get rid of states and just use sequences
User::factory()
->count(3)
->state(new Sequence(
['status' => 'verified'],
['status' => 'unverified'],
['status' => 'banned'],
))
->create()
2. Use variables as functions
Keep your current states, but dynamically access them from a predefined list.
$states = ['verified', 'unverified', 'banned'];
foreach ($states as $state) {
User::factory()->$state()->create();
}
3. Write each one out by hand.
Similar to number 2, but we don't use a loop.
User::factory()->verified()->create();
User::factory()->unverified()->create();
User::factory()->banned()->create();

How to properly send a sorted model from my controller to the Laravel API Resource collection?

This is my controller:
public function branchesNearby($lat, $lng)
{
$branches = Branch::all();
//Calculate distance between each branch and client
//Radius of earth is 6371 KM so multiply the result with radius of earth 6371*pi/180
foreach ($branches as $branch){
$branch['distance'] = sqrt(pow(($branch->lat - $lat), 2) + pow(($branch->lng - $lng), 2)) * 108;
}
//Sort by nearest first
$sortedBranches = $branches->sortBy('distance');
return BranchResource::collection($sortedBranches);
}
You can see i created an extra attribute to calculate distance between user location and the branch location. Then, I sort the branch model by distance. However, The api response that i get is:
API response
You can see it is an object. I don't need the keys "2", "0" and "1". I need to remove this extra wrapping and i need it to be an array of objects like this:
Correct API but without sorting
Surely, It is sorting which causes it to become an object? I tried many other ways, one of them being:
$sortedBranches = $collection->sortBy('distance');
$final = $sortedBranches->values()->toJson();
and sending this $final to the resource collection. That gives me the error: " call to a member function first() on string in file api resources". This must be something small but i really need help.
Updating:
I did not post my Resource before, this is how it is:
public function toArray($request)
{
return [
'id' => $this->id,
'shop' => $this->shop->name,
'shop_image' => asset('api/images/' . $this->shop->image_file),
'lat' => $this->lat,
'lng' => $this->lng,
'shop_logo' => asset('api/images/' . $this->shop->logo_file),
'distance' => $this->distance . " KM"
];
The error that i get if i use:
$sortedBranches = $branches->sortBy('distance')->values()->all();
return BranchResource::collection($sortedBranches);
is:
The error
Update 3:
If i don't call the resource collection and simply output the $sortedBranches like this:
return response()->json($sortedBranches, 200);
Here, the format of the api response is correct but the data is not correct. This is how it looks:
$sortedBranches
Is there a way i can manipulate $sortedBranches and show the output like with the BranchResource?
Based on our troubleshoot, the resulting solution should be:
return BranchResource::collection($sortedBranches)->values()->all();
When you pass a collection into the collect method of the BranchResource class, it re-creates the collection. Laravel sees you are returning an (collection)object directly from your controller, and intervenes by casting it to json( i believe this is default). If the resulting json is not converted the way you want, you need to modify this collection. Therfor we needed to modify the BranchResource::collection() collection, not the $sortedBranches collection.
Edit:
Modified collect method into collection;

Comparing laravel collections

I have two collections: "Instructions" and "Known". Basically I am taking a new set of "Instructions" and checking whether anything is different to what is "Known".
So, the quantity is not massive. I retrieve the info:
$Instructions = Instruction::all();
$Knowns = Known::all();
Now, I'm looking for the differences, and I've tried each of these three methods:
$IssuesFound = $Instructions->diff($Knowns);
$IssuesFound = $Instructions->diffKeys($Knowns);
$IssuesFound = $Instructions->diffAssoc($Knowns);
The thing is, an "Instruction" or "Known" is an item with 17 attributes, and anyone of those attributes can be different. I want to compare the attributes of an "Instruction" with the matching attribute of a "Known". (Both items have the same keys, bot items have a Reference attribute to act as a unique identifier.
What I'm finding is that theese methods give me the item that is different, but doesn't tell me which individual attributes are the mismatch.
foreach ($IssuesFound as $issue)
{
dd($issue);
}
So a method like $IssuesFound = $Instructions->diffKeys($Knowns); will come up with item xxx being different, but I can't see how to find out which attribute of the item it is that is different. Not unless I start nesting loops and iterating through all the attributes - which I'm trying to avoid.
How do I do it?
Thanks in advance. (Laravel 5.6)
Straight from laravel docs, diffAssoc will return what you are asking:
$collection = collect([
'color' => 'orange',
'type' => 'fruit',
'remain' => 6
]);
$diff = $collection->diffAssoc([
'color' => 'yellow',
'type' => 'fruit',
'remain' => 3,
'used' => 6
]);
$diff->all();
// ['color' => 'orange', 'remain' => 6]
You get the attribute from the FIRST collection that is different on the SECOND collection, therefore if you get 3 attributes when calling $diff->all() you will know WHICH attributes ARE DIFFERENT, so you could access them or do whatever you want to, if you post more specific results of what you are getting and what you are trying we can help, but I think you are just not thinking how to use these methods

Laravel attach pivot to table with multiple values

Background
I'm creating a database revolving around food allergies and I have a many to many relationship between foods and allergies. There is also a pivot value called severity which has a numerical number representing the severity of the allergy for that food item.
This link table looks like this;
food_id|allergy_id|severity
-------|----------|--------
1 | 1 | 3
1 | 4 | 1
2 | 2 | 1
The problem
When trying to update the link table with Eloquent (where $allergy_ids is an array)
$food->allergies()->attach($allergy_ids);
How would I go about adding multiple values to this pivot table at once along with the pivot values?
I can add all the allergy_id's for a particular food item in one go using the above line, but how can I also add in the severity column at the same time with an array of various severity values? Maybe something like
$food->allergies()->attach($allergy_ids, $severity_ids);
Edit: There could be between 0-20 allergies for a specific food item, and a severity rating from 0-4 for each allergy, if this helps at all.
You can.
From this example in Docs (4.2, 5.0):
$user->roles()->sync(array(1 => array('expires' => true)));
Hardcoded version for the first two rows:
$food = Food::find(1);
$food->allergies()->sync([1 => ['severity' => 3], 4 => ['severity' => 1]]);
Dynamically, with your arrays $allergy_ids and $severities in a compatible state (size and sort), you shall prepare your sync data before. Something like:
$sync_data = [];
for($i = 0; $i < count($allergy_ids); $i++))
$sync_data[$allergy_ids[$i]] = ['severity' => $severities[$i]];
$food->allergies()->sync($sync_data);
You can't do it like you' like so I suggest a simple loop:
foreach ($allergy_ids as $key => $id)
{
$food->allergies()->attach($id, array_get($severity_ids, $key));
// should you need a sensible default pass it as a 3rd parameter to the array_get()
}
workaround
However if you wanted to attach multiple allergies with single severity level/id then you could do this:
$food->allergies()->attach($allergy_ids, array('severity' => $singleSeverityValue));
From version 5.1 of Laravel (Currently in Laravel 9.x) onwards it is possible to pass an array as a second argument with all the additional parameters that need to be saved in the intermediate table.
As you can read in the documentation
When attaching a relationship to a model, you may also pass an array of additional data to be inserted into the intermediate table:
$user->roles()->attach($roleId, ['expires' => $expires]);
For convenience, attach and detach also accept arrays of IDs as input:
$user->roles()->attach([1 => ['expires' => $expires], 2, 3]);
Then you can simply do
$food->allergies()->attach([1 => ['severity' => 3], 4 => ['severity' => 1]]);
So, on Laravel 9, passing the ids in the array worked for me. Likeso,
$user->roles()->attach([$a->id,$b->id,$c->id]); and so on.
I guess instead of passing the string. We can pass just the id or else convert the string into array.
Easiest indeed is to attach with the extra data, like so:
$retailer->paymentmethods()->attach($paymentmethod, array('currency' => $paymentmethod->currency));
change out the values for food allergy severity, but you get the hint... :-)

Rails 4 and Mongoid: programmatically build query to search for different conditions on the same field

I'm building a advanced search functionality and, thanks to the help of some ruby fellows on SO, I've been already able to combine AND and OR conditions programmatically on different fields of the same class.
I ended up writing something similar to the accepted answer mentioned above, which I report here:
query = criteria.each_with_object({}) do |(field, values), query|
field = field.in if(values.is_a?(Array))
query[field] = values
end
MyClass.where(query)
Now, what might happen is that someone wants to search on a certain field with multiple criteria, something like:
"all the users where names contains 'abc' but not contains 'def'"
How would you write the query above?
Please note that I already have the regexes to do what I want to (see below), my question is mainly on how to combine them together.
#contains
Regex.new('.*' + val + '.*')
#not contains
Regex.new('^((?!'+ val +').)*$')
Thanks for your time!
* UPDATE *
I was playing with the console and this is working:
MyClass.where(name: /.*abc.*/).and(name: /^((?!def).)*$/)
My question remains: how do I do that programmatically? I shouldn't end up with more than two conditions on the same field but it's something I can't be sure of.
You could use an :$and operator to combine the individual queries:
MyClass.where(:$and => [
{ name: /.*abc.*/ },
{ name: /^((?!def).)*$/ }
])
That would change the overall query builder to something like this:
components = criteria.map do |field, value|
field = field.in if(value.is_a?(Array))
{ field => value }
end
query = components.length > 1 ? { :$and => components } : components.first
You build a list of the individual components and then, at the end, either combine them with :$and or, if there aren't enough components for :$and, just unwrap the single component and call that your query.

Resources