I am receiving the following error
[2018-12-18 12:12:46] local.ERROR: Credentials are required to create a Client {"exception":"[object] (Twilio\Exceptions\ConfigurationException(code: 0): Credentials are required to create a Client at C:\wamp64\www\_javid\javid\vendor\twilio\sdk\Twilio\Rest\Client.php:157)
I will include the code below and the source i used to create it. I would like to add, this was all working correctly the other evening.
Today, i merely added a new function to handle the saving of messages to the database. Then i started receiving the above error. Naturally i reverted my changes but still the same error.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\User;
use Illuminate\Support\Facades\Auth;
use JWTAuth;
use App\Item;
use Log;
use Twilio\Rest\Client;
class MessagingController extends Controller
{
protected $client;
public function __construct(Client $client){
$this->client = $client;
}
/**
* Show the form for creating a notification.
*
* #return \Illuminate\Http\Response
*/
public function create()
{
return view('notifications.create');
}
public function sendMessage(request $request){
$details = $request->only('membershipNumber', 'countryCode', 'message');
$user = User::where('membership_number', $details['membershipNumber'])->with('mobile_number')->first();
if(count($user)>0){
$this->messageSaveToDatabase($details, $user);
$this->messageSendToMobile($details, $user);
$this->messageSendToEmail($details, $user);
return response([
'status' => 'success',
'msg' => __('messages.success'),
'response' => $details
], 200);
} else {
return response([
'status' => 'error',
'msg' => __('messages.error')
], 200);
}
}
protected function messageSaveToDatabase($details, $user){
}
protected function messageSendToMobile($details, $user, $imageUrl = null){
$lineBreak = "\n\n";
$phoneNumber = $user->mobile_number->country_code.decrypt($user->mobile_number->number);
$message = "Hi member #".$details['membershipNumber'].$lineBreak.
$details['message'];
$twilioPhoneNumber = config('services.twilio')['phoneNumber'];
$messageParams = array(
'from' => $twilioPhoneNumber,
'body' => $message
);
if ($imageUrl) {
$messageParams['mediaUrl'] = $imageUrl;
}
$this->client->messages->create(
$phoneNumber,
$messageParams
);
}
protected function messageSendToEmail($details, $user){
}
}
I have checked the TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN, these are both correct.
The code was taken from the following guide, i stripped out the subscriber part. Guide from Twilio
one more thing, I found the following Here which suggests i need to do something like this $client = new Client($keySid, $keySecret, $accountSid); but the guide above, does not do this, plus it all worked like this also.
Any help or suggestions would be great, i'm running out of hair to pull out :(
After a little more googling and some re-working, I found a working solution
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\User;
use Illuminate\Support\Facades\Auth;
use Twilio\Rest\Client;
class MessagingController extends Controller
{
protected function messageSendToMobile($details, $message, $user, $imageUrl = null){
$accountSid = env('TWILIO_ACCOUNT_SID');
$authToken = env('TWILIO_AUTH_TOKEN');
$twilioNumber = env('TWILIO_PHONE_NUMBER');
$lineBreak = "\n\n";
$to = $user->mobile_number->country_code.decrypt($user->mobile_number->number);
$client = new Client($accountSid, $authToken);
try {
$client->messages->create(
$to,
[
"body" => $message,
"from" => $twilioNumber
]
);
Log::info('Message sent to ' . $twilioNumber);
} catch (TwilioException $e) {
Log::error(
'Could not send SMS notification.' .
' Twilio replied with: ' . $e
);
}
}
}
Related
Is my approach of return Exceptions and user object from the registration service to the controller and returning the result as json correct ? I am making RESTful API and I am not sure if I am on the right way. My controller:
<?php
namespace App\Controller;
use App\Service\RegistrationService;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Serializer\SerializerInterface;
class RegistrationController
{
public function __construct(
private RegistrationService $registrationService,
)
{
}
#[Route(path: '/api/v1/register', methods: 'POST')]
public function register(Request $request): JsonResponse
{
$registeredUser = $this->registrationService->register($request->request->all());
if ($registeredUser instanceof \Exception) {
return new JsonResponse([
'type' => 'error',
'content' => $registeredUser->getMessage(),
'status' => $registeredUser->getCode()
]);
}
// Here will serialize the $registeredUser object
// and will return it as content in the JsonResponse
return new JsonResponse([
'type' => 'success',
'content' => $serializedUser,
'status' => 200
]);
}
}
And my registration service:
<?php
declare(strict_types=1);
namespace App\Service;
use App\Entity\User;
use App\Repository\UserRepository;
use Symfony\Component\Validator\Validator\ValidatorInterface;
final class RegistrationService
{
public function __construct(
private UserRepository $userRepository,
private ValidatorInterface $validator
)
{
}
public function register(array $userData): User|\Exception
{
if (!$this->checkIfUserExists($userData['email'])) {
$user = new User(
$userData['email'],
$userData['password'],
$userData['referralCode']
);
$errors = $this->validator->validate($user);
if (count($errors) > 0) {
return new \Exception(message: (string)$errors, code: 400);
}
$this->userRepository->add($user, true);
return $user;
}
return new \Exception(message: 'User with this email already exists!', code: 409);
}
private function checkIfUserExists(string $email): bool
{
return $this->userRepository->findOneBy(['email' => $email]) !== null;
}
}
Will be thankful or any advises!
Yes that works. The best one I have seen is to have a middleware which has mapping between more specific error messages and less specific error messages. Before returning the exception to the user the middleware logs it and sends less specific errror messages to end user. Depending on your api, you may not want end user to see specific error messages but still might want to log them
In a Symfony 5.0.2 project a test of the new Mailer fails with
Error: Call to a member function getSubject() on null
The email service and test are based on symfonycast tutorials.
Adding var_dump($email); in the service immediately after $email = ...; shows object(Symfony\Bridge\Twig\Mime\TemplatedEmail)#24 (11) {..., which says there is a real object created in the service.
services.yaml:
App\Services\EmailerService:
$mailer: '#mailer'
$senderAddress: '%app.sender_address%'
Service:
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
class EmailerService
{
private $mailer;
private $sender;
public function __construct($mailer, $senderAddress)
{
$this->mailer = $mailer;
$this->sender = $senderAddress;
}
public function appMailer($mailParams)
{
$email = (new TemplatedEmail())
->from($this->sender)
->to($mailParams['recipient'])
->subject($mailParams['subject'])
->htmlTemplate($mailParams['view'])
->context($mailParams['context']);
$this->mailer->send($email);
}
}
Test:
use App\Services\EmailerService;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mailer\MailerInterface;
class MailerTest extends TestCase
{
public function testSimpleMessage()
{
$symfonyMailer = $this->createMock(MailerInterface::class);
$symfonyMailer->expects($this->once())
->method('send');
$mailer = new EmailerService($symfonyMailer, 'admin#bogus.info', 'admin#bogus.info');
$mailParams = [
'view' => 'Email/non_user_forgotten_password.html.twig',
'context' => ['supportEmail' => 'admin#bogus.info'],
'recipient' => 'bborko#bogus.info',
'subject' => 'Test message',
];
$email = $mailer->appMailer($mailParams);
$this->assertSame('Test message', $email->getSubject());
}
}
appMailer() must return a TemplatedEmail object so you can call getSubject() on it. Currently it is returning nothing. Change it to:
public function appMailer($mailParams)
{
$email = (new TemplatedEmail())
->from($this->sender)
->to($mailParams['recipient'])
->subject($mailParams['subject'])
->htmlTemplate($mailParams['view'])
->context($mailParams['context']);
$this->mailer->send($email);
return $email; // I added this line.
}
First I will explain why and how the solution works and then the problems I have encountered. If you think there is a better way to do what I do, I'd love to hear it. I would also like to know why doctrine behaves in this way.
It turns out that my aplication needs to connect to a different database according to the client. I have a table, in a fixed database, containing the connection information that is used in some request.
I have had success with the following code:
class DynamicEntityManager {
protected $em;
private $request;
private $client_id;
public function __construct(RequestStack $request, EntityManagerInterface $em){
$this->em = $em;
$this->request = $request;
}
public function getEntityManager(ClientConn $client = null) {
$request = $this->request->getCurrentRequest();
if($client == NULL){
$domain = $request->attributes->get('domain');
if($domain == "" || $domain == NULL){
throw new \Exception("Error de conexion", 1);
}
$client = $this->em->getRepository(ClientConn::class)->findOneBy(array(
"subdomain" => $domain
));
if($client == NULL){
throw new \Exception("Error de conexion", 1);
}
}
$connectionDB = $client->getConnection();
$dbdriver = 'oci8';
$conexionSplit = explode(':',$connectionDB);
$dbhost = $conexionSplit[0];
$dbport = $conexionSplit[1];
$dbname = $conexionSplit[2];
$dbuser = $client->getUsuarioBd();
$dbpass = $client->getClaveBd();
$service = false;
$this->client_id = $client->getId();
if(strpos($dbname,'SN=') !== false){
$parts = explode('=',$dbname);
$dbname = $parts[1];
$service = true;
}
$request->attributes->set('client_id',$client->getId());
$conn = array(
'driver' => $dbdriver,
'host' => $dbhost,
'port' => $dbport,
'dbname' => $dbname,
'user' => $dbuser,
'password' => $dbpass,
'service' => $service,
'charset' => 'UTF8',
'schema' => null
);
return EntityManager::create($conn, $this->em->getConfiguration());
}
}
As you can see I return EntityManager::create($conn, $this->em->getConfiguration ()) with the new connection. The way I use it is the next:
/**
* #Route("/api/client/{id}/conf/{confID}", name="conf.show")
* #Method({"GET"})
*/
public function show(ClientConn $client, Request $request, DynamicEntityManager $dem ,$confId){
try {
$em = $dem->getEntityManager($client);
$entity = $em->getRepository(Configuration::class)->find($confId);
return new JsonResponse($entity, 200);
}
catch(\Exception $ex) {
return new JsonResponse([
"excepcion" => $ex->getMessage()
], $ex->getCode());
}
}
It works as expected or so I believed until I saw that when the entity has a custom repository it is unable to use the dynamic connection and therefore the previous route will return a table not found exception.
#ORM\Entity() <-- Works like a charm
#ORM\Entity(repositoryClass="App\Repository\ConfigurationRepository")<-- Table not found.
It works in the repository if I create the connection again, although I do not like the solution. So, what do I want? I would like to be able to use the basic methods like find (), findBy () and others without having to rewrite them every time I use a custom repository.
class ConfigurationRepository extends ServiceEntityRepository
{
public function __construct(RegistryInterface $registry, DynamicEntityManager $dem)
{
parent::__construct($registry, Configuration::class);
$this->dem= $dem;
}
public function uglyFind($client, $confID)
{
$query = $this->dem->getEntityManager($client)->createQueryBuilder('conf')
->select("conf")
->from(ConfPedidosLentes::class,'conf')
->where('conf.id = :value')->setParameter('value', $confID)
->getQuery();
return $query->getOneOrNullResult();
}
I will really appreciate any contribution and thought in this matter.
Instead of:
class ConfigurationRepository extends ServiceEntityRepository
{
public function __construct(RegistryInterface $registry, DynamicEntityManager $dem)
{
parent::__construct($registry, Configuration::class);
$this->dem= $dem;
}
...
try extending EntityRepository (without using a constructor) and use find as you did in your controller:
use Doctrine\ORM\EntityRepository;
class ConfigurationRepository extends EntityRepository
{
}
ServiceEntityRepository is an optional EntityRepository base class with a simplified constructor for autowiring, that explicitly sets the entity manager to the EntityRepository base class. Since you have not configured your doctrine managers to handle these connections properly (it's not even possible actually with so many connections), ServiceEntityRepository will pass a wrong EntityManager instance to the EntityRepository subclass, that's why you should not extend ServiceEntityRepository but EntityRepository.
I'm trying to get all the errors from all the queries I do in my project and redirect this errors to a controller called "error" that will treat theses errors as I want. The problem looks like when I redirect all the information goes in the url generated by the function via GET.
I suppose that if this information is sent via POST will disappear this problem but I'm not using obviously any form inside the controller. So, how can I say to the redirect function that these information shouldn't go with the url and instead should go via POST?
Is possible what I'm trying to do?
Inside Controllers:
try {
$results = $queries->aQuery();
} catch (ErrorException $errorException) {
return $this->redirect($errorException->redirectResponse);
}
Inside the service query:
public function aQuery(){
$query="SELECT * FROM blabla ...";
try {
$stmt = $this->DB->->prepararQuery($query);
$stmt->execute();
$results = $stmt->fetchAll();
} catch (DBALException $DBALException) {
$errorException = new ErrorException($this->router->generate('error',
[
'errorQuery' => $query,
'errorData' => "0 => '".$data1."', 1 ....",
'errorOrigin' => 'a place',
'errorResponseText' => $DBALException->getMessage()
]
));
throw $errorException;
}
}
The ErrorException:
class ErrorException extends \Exception
{
/**
* #var \Symfony\Component\HttpFoundation\RedirectResponse
*/
public $redirectResponse;
/**
* ErrorException constructor.
* #param \Symfony\Component\HttpFoundation\RedirectResponse $redirectResponse
*/
public function __construct(string $redirectResponse)
{
$this->redirectResponse = $redirectResponse;
}
}
If what you are trying to achieve is a centralized way to handle exceptions have a look at https://symfony.com/doc/4.0/event_dispatcher.html#creating-an-event-listener and use kernel.exception event
public function onKernelException(GetResponseForExceptionEvent $event)
{
if (! $event->getException() instanceof ErrorException) {
return;
}
// handle your custom ErrorException
$response = new Response();
$response->setContent($event->getException()->getMessage());
// sends the modified response object to the event
$event->setResponse($response);
}
I'm still very new to symfony and really enjoying it.
I'm at the stage where I've managed to create and setup a service, the service itself uses 2 dependencies:
A Data API that returns json data (this is a separate library which
i have implemented as a service and came with its own unit tests).
The Doctrine Entity Manager.
The service pulls the data required using the api and then loops through the data and checks to see if the data already exists, if it does it updates the existing entity and persists it, Otherwise it creates a new entity assigns the data and persists it.
I now need to write a unit test for this, i've not used PHPUnit only from symfony2 tutorials which were testing responses from a controller.
How do i go about writing a unit test for this service?
In particular mocking the data that i would normally pull from the api.
and then checks to see if the entry needs to be updated or created?
A code example would be really helpful so i can then use this as a template to create tests for other similar services that i create.
Here's the Service i want to test:
<?php
namespace FantasyPro\DataBundle\DataManager;
use Doctrine\ORM\EntityManager;
use FantasyDataAPI\Client;
use FantasyPro\DataBundle\Entity\Stadium;
class StadiumParser {
/**
* #var EntityManager $em
*/
private $em;
/**
* #var Client $client
*/
private $client;
public function __construct( EntityManager $em, Client $client) {
$this->em = $em;
$this->client = $client;
}
/**
* #return array
*/
public Function parseData(){
//var_dump($this);
$stadiumData = $this->client->Stadiums();
//var_dump($stadiumData);
//get the Repo
$repo = $this->em->getRepository('DataBundle:Stadium');
$log = array();
foreach ($stadiumData as $stadium) {
// Get the current stadium in the list from the database
$criteria = array( 'stadiumID' => $stadium['StadiumID'] );
$currentStadium = $repo->FindOneBy( $criteria );
if ( ! $currentStadium) {
$currentStadium = new Stadium(); //no stadium with the StadiumID exists so create a new stadium
$logData = [
'action' => 'Added Stadium',
'itemID' => $stadium['StadiumID'],
'itemName' => $stadium['Name']
];
$log[] = $logData;
} else {
$logData = [
'action' => 'Updated Stadium',
'itemID' => $stadium['StadiumID'],
'itemName' => $stadium['Name']
];
$log[] = $logData;
}
$currentStadium->setStadiumID( $stadium['StadiumID'] );
$currentStadium->setName( $stadium['Name'] );
$currentStadium->setCity( $stadium['City'] );
$currentStadium->setState( $stadium['State'] );
$currentStadium->setCountry( $stadium['Country'] );
$currentStadium->setCapacity( $stadium['Capacity'] );
$currentStadium->setPlayingSurface( $stadium['PlayingSurface'] );
$this->em->persist( $currentStadium );
}
$this->em->flush();
return $log;
}
}
****** UPDATE *******
after reading ilpaijin's answer.
I've simplified the service so it no longer returns a log, i initially had this in so i could check what had been added by sending the log to a twig template in my controller, I eventually plan to have this running as a command so i can run a it via a cron job so the log bit is unnecessary.
I'm now setting the entity within my construct as i couldn't work out how to pass an entity as a injected dependency.
Now grabbing a new entity by using createNewStadium() method.
The Updated Service:
namespace FantasyPro\DataBundle\DataManager;
use Doctrine\ORM\EntityManager;
use FantasyDataAPI\Client;
use FantasyPro\DataBundle\Entity\Stadium;
class StadiumParser {
/**
* #var EntityManager $em
*/
private $em;
/**
* #var Client $client
*/
private $client;
/**
* #var Stadium Stadium
*/
private $stadium;
public function __construct( EntityManager $em, Client $client) {
$this->em = $em;
$this->client = $client;
}
/**
* Gets a list of stadiums using $this->client->Stadiums.
* loops through returned stadiums and persists them
* when loop has finished flush them to the db
*/
public Function parseData(){
$data = $this->client->Stadiums();
//get the Repo
$repo = $this->em->getRepository('DataBundle:Stadium');
foreach ($data as $item) {
// Get the current stadium in the list
$criteria = array( 'stadiumID' => $item['StadiumID'] );
$currentStadium = $repo->FindOneBy( $criteria );
if ( ! $currentStadium) {
$currentStadium = $this->createNewStadium; //no stadium with the StadiumID use the new stadium entity
}
$currentStadium->setStadiumID( $item['StadiumID'] );
$currentStadium->setName( $item['Name'] );
$currentStadium->setCity( $item['City'] );
$currentStadium->setState( $item['State'] );
$currentStadium->setCountry( $item['Country'] );
$currentStadium->setCapacity( $item['Capacity'] );
$currentStadium->setPlayingSurface( $item['PlayingSurface'] );
$this->em->persist( $currentStadium );
}
$this->em->flush();
}
// Adding this new method gives you the ability to mock this dependency when testing
private function createNewStadium()
{
return new Stadium();
}
}
What you basically need is Unit Testing the service using what is called "Test doubles".
This means you should mock the dependencies your service has, this way you are able to test only the service in isolation without really relying on the deps but only on a mocked version of them, with hardcoded values or behaviour.
A true example based on your real implementation isn't possible since you have tight coupled deps as $currentStadium = new Stadium();. You should pass deps like these in a constructor or via getter/setter in order to be able to mock it when Unit testing.
Once done it a very indicative example would be:
// class StadiumParser revisited and simplified
class StadiumParser
{
private $client;
public function __construct(Client $client)
{
$this->client = $client;
}
public function parseData()
{
$stadiumData = $this->client->Stadiums();
// do something with the repo
$log = array();
foreach ($stadiumData as $stadium) {
$logData = [
'action' => 'Added Stadium',
'itemID' => $stadium['StadiumID'],
'itemName' => $stadium['Name']
];
$log[] = $logData;
}
// do something else with Doctrine
return $log;
}
}
and the test
// StadiumParser Unit Test
class StadiumParserTest extends PHPUnit_Framework_TestCase
{
public function testItParseDataAndReturnTheLog()
{
$client = $this->getMock('FantasyDataAPI\Client');
// since you class is returning a log array, we mock it here
$expectedLog = array(
array(
'action' => 'Added Stadium',
'itemID' => $stadium['StadiumID'],
'itemName' => $stadium['Name']
)
);
// this is the mocked or test double part.
// We only need this method return something without really calling it
// So we mock it and we hardcode the expected return value
$stadiumData = array(
array(
"StadiumID" => 1,
"Name" => "aStadiumName"
)
);
$client->expects($this->once())
->method('Stadiums')
->will($this->returnValue($stadiumData));
$stadiumParser = new StadiumParser($client);
$this->assertEquals($expectedLog, $stadiumParser->parseData());
}
}
I voluntarily omitted the EntityManager part because I guess you should have a look at the Symfony Doc relative to how to unit test code interacting with the database
-----EDIT2-----
Yes he was right, you shouldn't. One possible way that comes in mind is to extract the creation of the entity in a protected/private method. Something like:
// class StadiumParser
public Function parseData()
{
...
foreach ($stadiumData as $stadium) {
...
if ( ! $currentStadium) {
$currentStadium = $this->createNewStadium();
...
}
// Adding this new method gives you the ability to mock this dependency when testing
private function createNewStadium()
{
return new Stadium();
}
-----EDIT3-----
I want to suggest you another approach. This should be, probably, a better choice should the Stadium entity needed in different services or different part of the same. What I'm proposing is called Builder pattern but a Factory could also be an option here. Browse a bit for their differences.
As you can see this extract some code from the method, distribute better the responsibility between the classes and leaves all cleaner and easier to read for you and your teammates. And you already know how to mock it when testing.
class StadiumParser
{
private $stadiumBuilder;
...
public function __construct( StadiumBuilder $builder, ...) {
$this->stadiumBuilder = $stadiumBuilder;
...
}
public Function parseData()
{
...
foreach ($stadiumData as $stadium) {
...
$currentStadium = $repo->FindOneBy( $criteria );
if ( ! $currentStadium) {
$currentStadium = $this->stadiumBuilder->build($currentStadium, $stadium);
}
$this->em->persist($currentStadium);
...
somewhere you have this new Builder that return a Stadium instance. this way your StadiumParser service is not coupled anymore with the entity, but the StadiumBuilder is it. The logic is something like:
// StadiumBuilder class
namespace ???
use FantasyPro\DataBundle\Entity\Stadium;
class StadiumBuilder
{
// depending on the needs but this should also has a different name
// like buildBasic or buildFull or buildBlaBlaBla or buildTest
public function build($currentStadium = null, $stadium)
{
if (!$currentStadium) {
$currentStadium = new Stadium();
}
$currentStadium->setStadiumID( $stadium['StadiumID'] );
$currentStadium->setName( $stadium['Name'] );
$currentStadium->setCity( $stadium['City'] );
$currentStadium->setState( $stadium['State'] );
$currentStadium->setCountry( $stadium['Country'] );
$currentStadium->setCapacity( $stadium['Capacity'] );
$currentStadium->setPlayingSurface( $stadium['PlayingSurface'] );
return $currentStadium;
}
}