Updating a collection and returning it - laravel

I want to update a collection before looping through it.
Is there a way to set the sent value of the emails collection to true and return the collection before actually looping through it.
$emails = Email::where('sent', false);
$emails->update(array("sent" => true));
foreach ($emails as $email) {
//send email
}
I want to be able to call ->get() somewhere but I cant do it on the update or the initial query

Don't forget to call get on the Email::where('sent', false) that is an Illuminate\Eloquent\Builder instance.
You can use something like this:
tap(Email::where('sent', false)->get(), function ($emails){
Email::whereIn('id', $emails->pluck('id')->all())->update(['sent' => true]);
})->each(function($email){
// Send email
});
The tap function accepts two arguments, first one is a value that you want to run the second argument(which is a callback) on it, then the tap function returns you that first passed value and here you can easily loop through.
See Here for tap

Related

Calling at the users at once and disconnect other calls if any one of the user receives the call

I want to have the round robin call functionality using twilio.
Let's say. I have 100 users and their phone numbers.
I want to call of them at the same time.
Then whoever the first person receives the call I will connect that call to my sales department and immediately cut or disconnect a the other calls.
I known through twiML I could dial to my sales team and I also know I could check the in-progess event to check to see call is connected.
However I am stuck at calling all my users at the same time and disconnecting after the first user is connected to the call which is my first step.
i am making more updates as my scenario has been changed a little.
in this case i am first calling the user who has filled out my lead form. once the lead user receives the call then i am going to call 10 agents from my sales team but i want to track which agent has received the lead call and want to save the agent information into my database and cut the other calls.
in my countroller
<?php
namespace App\Http\Controllers;
use App\Listing;
use App\User;
use Illuminate\Http\Request;
use Twilio\Rest\Client;
use Twilio\TwiML\VoiceResponse;
use Twilio\Twiml;
class TwilioController extends Controller
{
public function leadCall(Request $request)
{
// Lead user
$lead = Lead::where('id', $request->lead_id)->first();
$country_code = "+1";
$visitor_phone = $country_code . $lead->details_phone;
$url = "https://www.samplewebsite.com/outbound?multi_call=true";
// Twilio Credentials
$AccountSid = 'xyz';
$AuthToken = 'xyz';
$twilio_number = "123";
$client = new Client($AccountSid, $AuthToken);
// Calling the lead visitor first
try {
$call = $client->account->calls->create($visitor_phone, $twilio_number,
array(
"url" => $url
)
);
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
}
}
if($request->multi_call)
{
// Fetch 10 agents
$users = User::all()->take(10);
// Calling Twilio
$twiml = new VoiceResponse();
$twiml->say('Please hold, we are connecting your call to one of our agent.', array('voice' => 'alice'));
$dial = $twiml->dial();
foreach ($users as $user) {
if($user->phone && $user->live_call)
{
$dial->number($user->phone,
['statusCallbackEvent' => 'answered',
'statusCallback' => 'https://www.samplewebsite.com/outbound?agent_call=true',
'statusCallbackMethod' => 'GET']);
}
}
$response = response()->make($twiml, 200);
$response->header('Content-Type', 'text/xml');
return $response;
}
if($request->agent_call)
{
return "Call was made to: " . $request->to;
}
}
this is what i have done so far
and in routes file
Route::post('lead-call', 'TwilioController#leadCall')->name('leadCall');
Route::get('outbound', 'TwilioController#outboundMultiCall');
And TwiML resonse
<Response>
<Say voice="alice">Please hold, we are connecting your call to one of our agent.</Say>
<Dial>
<Number statusCallbackEvent="answered" statusCallback="https://www.samplewebsite.com/outbound?agent_call=true" statusCallbackMethod="GET">xxx-xxx-xxxx</Number>
<Number statusCallbackEvent="answered" statusCallback="https://www.samplewebsite.com/outbound?agent_call=true" statusCallbackMethod="GET">xxx-xxx-xxxx</Number>
<Number statusCallbackEvent="answered" statusCallback="https://www.samplewebsite.com/outbound?agent_call=true" statusCallbackMethod="GET">xxx-xxx-xxxx</Number>
</Dial>
</Response>
i am having two problems. when i add the get parameter like multi_call=true in my webhook then i get application error. so i can't make 10 dials to my agents.
plus i want to keep track of which user has attend the call first so i could maintain into my database and increase their rating.
Thank you
Twilio developer evangelist here.
First, I just need to warn you that by default Twilio accounts have a limit of creating 1 call per second, so with any code, you calls will still be placed sequentially.
If you can make one call then you can make more than one call at a time. You need to loop through the numbers you want to place calls to and make an API request for each call you want to create.
The next part is to cancel the other calls once one call connects. When you create the call, you will receive a response from the API with the call SID. Once your first call connects, you will receive a webhook to your application. The parameters will include the call SID. So, to cancel the other calls you need to take the list of call SIDs that you have created remove the one that connected and then make API calls to update the the other calls to the "completed" status, which will hang up the call.
Edit:
I see that you're connecting your outbound call first, then using TwiML to multi-dial. That should work, there's just a couple of things you've done wrong.
First, you are using multiple <Dial>s instead of multiple <Number>s within a <Dial> to make the multi calls. Try the following:
public function outboundMultiCall(Request $request)
{
if($request->multi_call)
{
// Fetch 10 agents
$users = User::all()->take(10);
// Calling Twilio
$twiml = new VoiceResponse();
$twiml->say('Please hold, we are connecting your call to one of our agent.', array('voice' => 'alice'));
$dial = $twiml->dial();
foreach ($users as $user) {
$dial->number($user->phone);
}
$response = response()->make($twiml, 200);
$response->header('Content-Type', 'text/xml');
return $response;
}
return 'Some other action based on GET URL paramter';
}
Second, Twilio webhooks are POST requests by default. So, you should either turn your route into a POST:
Route::post('outbound', 'TwilioController#outboundMultiCall');
Or, you can pass a method parameter when you create the call:
$call = $client->account->calls->create($visitor_phone, $twilio_number,
array(
"url" => $url,
"method" => "GET"
)
);
Finally, to record who answers the call you can use a statusCallback URL attribute on the <Number>. Twilio will send a webhook when the call transitions into a new state. The events are "initiated", "ringing", "answered" and "completed". The webhook will include all the normal voice call parameters so you can tell who the call was made to with the To parameter. There are extra parameters too, which might be useful.

Laravel filtered collection is no longer filtered after json encoding

I have an Eloquent collection with an eager loaded relationship. When I filter this eager loaded relationship, it works fine, but if I encode it as JSON (to pass it to my front end Javascript framework), the collection is no longer filtered.
Simplified example:
$questions = Question::with('variables')->get();
foreach($questions as $key => $q) {
$questions[$key]->variables = $q->variables->reject(function($v) {
return $v->type == 3;
});
}
dd($questions);
If I look at the $questions variable at this point, my collection is correctly filtered. If, however, I add json_decode(json_encode($questions)) following line before dumping, the collection is no longer filtered.
Note that in my real application, I have to do some things with the rejected variables before throwing them out of the collection, so I cannot simply filter them out during the query.
My workaround right now is to json encode and decode the collection, then do an array filter to get rid of the variables I do not want to pass to the front end. This works, but seems like a terribly inelegant and unnecessary solution. Am I doing something wrong or is this the expected behavior?
I'm still running Laravel 5.8 on this application in the event that this behavior has changed on newer versions.
Why not just load the variables twice then?
$questions = Question::with(['variables' => fn($v) => $v->where('type', '!=', 3)])->get();
// do whatever you need to do with the filtered collection
// reload variables relationship
$questions->load('variables');
// do whatever you need to do with the question and all its variables.
You can try
$questionsWithFilteredVariables = $questions->map(function($question) {
$variables = $question->variables->reject(fn($var) => $var->type === 3);
unset($question->variables);
$question->variables = $variables;
return $question;
});
//Now do json_decode(json_encode(...)), it will still contain filtered variables
$questionsWithFilteredVariables = json_decode(
json_encode($questionsWithFilteredVariables)
);

Laravel - can't get model after deleting related model records

I have a function postShippingmethods in which I delete, update or create new shipping methods depending what the user specified in the form. It works like it should , except that if any methods are deleted, I get empty result when trying to retrieve the user model with the updated methods.
To delete methods, I first get the user:
$user = \App\User::where('id',$id)->with(['shipping_profiles' => function($query) {
$query->where('default', 1);
}, 'shipping_profiles.methods' ])->first();
I compare the stored methods to the ones in the request; if any are missing , they should be deleted.
This code does the deletion:
foreach($non_included_ids as $id){
$method = \App\ShippingMethod::find($id);
$method->suppliers()->detach();
$method->delete();
}
Then I get the user once again, to get the updated data:
$user = \App\User::where('id',$id)->with(['shipping_profiles' => function($query) {
$query->where('default', 1);
}, 'shipping_profiles.methods' ])->first();
^^ this works well if nothing was deleted, but for some reason if something was deleted, the above code will return nothing. And when I try to use the $user to retrieve data I of course get "Trying to get property of non object".
So can anyone explain why this happen and what I should do to get the user with the updated data?
Use a different variable name in the loop:
foreach($non_included_ids as $non_included_id){
Otherwise, the loop changes the value of $id and \App\User::where('id',$id) fetches the wrong user.

Laravel Eloquent can increment ($collection->increment('field')) fail?

I have a redirect function that looks like this. For some reason, it looks like it's sometimes missing to increment the item. I'm wondering if it sometimes can be so that it misses this because some caching or something?
public function redirect($key)
{
$item = $this->items->findOrFail($key);
$item->increment('redirects');
$encode = urlencode($item->_url);
return view('item.redirect', ['url' => ($encode)]);
}
I'm not sure about topic of your question. Do you get such method somewhere from Laravel?
I don't know what is $key here but you should make sure this is single id (for example 2) and not array.
For single model:
$item->increment('redirects');
will work
but if $key sometimes is array then
$item = $this->item->findOrFail($key);
will be collection, so increment method would cause error, because there is no increment method on Eloquent collection.

CakePHP beforeFind

I am using cake's callback method, beforeFind which adds some conditions to each query dynamically. All went well at first and thought it will minimize development time.. until I made an ajax request which performs a find on some models.. It doesn't seem to work when this happens..
Basically I am adding some query conditions
public function beforeFind($query){
if(empty($this->aff)){
throw new Exception("Username cannot be null.");
}
//set active flag
$query["conditions"]["MsGa.active_flag"] = "active";
if(empty($query["conditions"]["OR"])){
$query["conditions"]["OR"] = array();
}
$query["conditions"]["OR"][] = array("FIND_IN_SET('".$this->aff."', MsGa.aff)");
$query["conditions"]["OR"][] = array("MsGa.aff" => "");
if(!empty($this->type)){
$query["conditions"]["MsGa.type"] = $this->type;
}
return $query;
}
Would it normally work in an ajax request, when a find is done on a model (ofcourse.. )? If not how so? Is it possible to explicitly invoke it?
[EDIT]
Here's the function that is called when the ajax request is initiated :
public function getResultsCount($data){
return $this->find("count", array(
"conditions" => $this->_setupConditions($data)
));
}
And
_setupConditions returns an array of query conditions...
The function which returns the result set :
public function getResults($data){
return $this->find("all", array(
"conditions" => $this->_setupConditions($data)
));
}
Whenever I initiate the ajax request and the query is executed, a certain count is returned. And when the actual result set is returned, there are less records then actually displayed by the count function. . So my thinking was that it might be that the beforeFind() is not being executed, since if I comment out this callback, the count and the number of records on the result set are equal. I am a bit baffled

Resources