How to cache model attributes in Laravel - laravel

In my current configuration, a user's email is stored on a remote server that I need to hit with a curl quest.
Luckily, I only need the email once a day when a certain process runs. However, when that process does run it will need to reference the email multiple times.
This is the current accessor I have set up for email. The problem is the curl request is being called every time I use $user->email. What's the best way to avoid this?
in UserModel:
public function getEmailAttribute(){
$curl = new Curl;
$responseJson = $curl->post('https://www.dailycred.com/admin/api/user.json',array(
'client_id'=>getenv('dailycredId')
,'client_secret'=>getenv('dailycredSecret')
,'user_id'=>$this->id
));
$response = json_decode($responseJson);
return $response->email;
}

private $cached_email = false;
public function getEmailAttribute(){
if ($this->cached_email){
// if set return cached value
return $this->cached_email;
}
// get the email
$curl = new Curl;
$responseJson = $curl->post('https://www.dailycred.com/admin/api/user.json',array(
'client_id'=>getenv('dailycredId')
,'client_secret'=>getenv('dailycredSecret')
,'user_id'=>$this->id
));
$response = json_decode($responseJson);
// cache the value
$this->cached_email = $response->email;
// and return
return $this->cached_email;
}
Depending on your use case make adjustments (ie. session, cache , static property...).

Extend a the Eloquent Model class
namespace App\Models\Utils;
use Illuminate\Database\Eloquent\Model as OldModel;
class MyModel extends OldModel
{
private $cachedAttributes = [];
public function getCachedAttribute(string $key, Callable $callable)
{
if (!array_key_exists($key, $this->cachedAttributes)) {
$this->setCachedAttribute($key, call_user_func($callable));
}
return $this->cachedAttributes[$key];
}
public function setCachedAttribute(string $key, $value)
{
return $this->cachedAttributes[$key] = $value;
}
public function refresh()
{
unset($this->cachedAttributes);
return parent::refresh();
}
}
make your class
class ElementWithEmail extends MyModel
{
const ATTRIBUTE_KEY_FOR_EMAIL = 'Email';
public function getEmailAttribute(){
$key = self::ATTRIBUTE_KEY_FOR_EMAIL;
$callable = [$this, 'getEmail'];
return $this->getCachedAttribute($key, $callable);
}
protected function getEmail()
{
$curl = new Curl;
$responseJson = $curl->post('https://www.dailycred.com/admin/api/user.json',array(
'client_id'=>getenv('dailycredId')
,'client_secret'=>getenv('dailycredSecret')
,'user_id'=>$this->id
));
$response = json_decode($responseJson);
return $response->email;
}
}
Call it from your code
$element = new ElementWithEmail();
echo $element->email;

Related

Laravel Create a request internally Resolved

I need to recreate a resquest so that it behaves like a call via api to go through the validator, but my $request->input('rps.number') always arrives empty, although I can see the data in the debug
I also couldn't get it to go through the laravel validator
I can't use a technique to make an http call, because I need to put this call in a transaction
<?php
$nota = new stdClass();
$rps = new stdClass();
$rps->numero = (int)$xml->Rps->IdentificacaoRps->Numero;
$rps->serie = (string)$xml->Rps->IdentificacaoRps->Serie;
$rps->tipo = (int)$xml->Rps->IdentificacaoRps->Tipo;
$nota->rps = $rps;
$controller = new NotaController(new Nota());
$content = new StoreNotaRequest();
$content->request->add($nota);
$result = $controller->store($content);
StoreNotaRequest
<?php
class StoreNotaRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
$request = $this->request;
return [
'rps.numero' => 'required_with:rps|numeric|between:1,999999999999999',
'rps.serie' => 'required_with:rps|string|min:1|max:5',
'rps.tipo' => 'required_with:rps|integer|in:1,2,3'
];
}
}
NotaController
<?php
class NotaController extends Controller
{
private Nota $nota;
public function __construct(Nota $nota)
{
$this->nota = $nota;
}
public function store(StoreNotaRequest $request): JsonResponse
{
// $validated = $request->validated();
try {
$nota = DB::transaction(function () use ($request) {
$request->input('rps.numero');
});
return response()->json($nota);
} catch (Throwable $e) {
return response()->json($data, 409);
}
}
}
Solution
the solution was a little too verbose, I believe it is possible to solve with less code.
more does what it needs to go through the validation of the data contained in the StoreNotaRequest
and it returns an http response, in addition to being able to put all these isolated calls in a single transaction
DB::beginTransaction();
$errors = [];
foreach ($itens as $item) {
$controller = new NotaController(new Nota());
$request = new StoreNotaRequest();
$request->setMethod('POST');
$request->request->add($nota);
$request
->setContainer(app())
->setRedirector(app(Redirector::class))
->validateResolved();
$response = $controller->store($request);
if ($response->statusText() !== 'OK') {
$errors[$item->id] = 'ERROR';
}
}
if (count($errors) === 0) {
DB::commit();
} else {
DB::rollBack();
}

Post with xml file in laravel

I am using method post to create new data in xml file but The function c_element cannot be used in the function store
$DeTai = c_element('DeTai', $root);
This is my current code:
public function c_element($e_name, $parent)
{
global $xml;
$node = $xml->createElement($e_name);
$parent->appendChild($node);
return $node;
}
public function c_value($value, $parent)
{
global $xml;
$value = $xml->createTextNode($value);
$parent->appendChild($value);
return $value;
}
public function store(Request $request)
{
$xml = new DOMDocument("1.0","UTF-8");
$xml->load('../xml/QuanLyDoAnTotNghiep.xml');
if ($request->isMethod('post'))
{
$madt= $request->madt;
$noidungdetai = $request->noidungdetai;
$root=$xml->getElementsByTagName("QuanLyDoAnTotNghiep")->item(0);
$DeTai = c_element("DeTai", $root); //error in here
$s_madt = c_element('MaDT', $DeTai);
c_value("$madt", $s_madt);
$s_noidungdetai = c_element('NoiDungDeTai', $DeTai);
c_value("$noidungdetai", $s_noidungdetai);
$xml->formatOutput=true;
$xml->save('../xml/QuanLyDoAnTotNghiep.xml');
echo "Thêm mới thành công!!!";
}
}
use this keyword to call one method in different method of same class
$DeTai = $this->c_element('DeTai', $root);
to know more about it please visit this
Thanks..

table relationship and how to use it in laravel controller

so, I have 2 tables, stage and event. Stage hasMany event, and Event belongsTo Stage. And I want to show all stage and its event as json. Here is my code in controller:
public function getschedule(){
$schedule = Stage::all();
//$event = Event_schedule2020::all();
if (!$schedule) {
return response()->json(['msg'=>'Error not found','code'=>'404']);
}
foreach($schedule->events as $array){
$datax[] = [
'id'=>$array->id,
'time'=>$array->time,
'category'=>$array->category,
'type'=>$array->title,
'designer'=>$array->designer,
];
}
foreach ($schedule as $item) {
$jadwal[] = [
'id'=>$item->id,
'date'=>$item->date,
'place'=>$item->stage,
'data'=>$datax,
];
}
return response()->json($jadwal);
}
but I always get this error
the error
so, is there anything I can do about this?
You can utilize inbuilt functions to do what you want to. Laravel automatically transforms model into JSON, no need to built arrays with it.
public function getschedule() {
// tell laravel you want to eager load events
$stages = Stage::with('events')->get();
// laravel knows you loaded events and therefor you can just return it and it does the rest automatically
return response()->json($stages);
}
in your Stage model you have to create relationship like this
public function events()
{
return $this->hasMany('App\Event');
}
then in your Controller
public function getschedule(){
$schedules = Stage::with('events')->get()->toArray();
return response()->json($schedules );
}
your mistake is call events on a collection for solve this you can change foreach like followings :
public function getschedule(){
$schedules = Stage::all(); // I add a 's' to $schedule because is better set plural name;
if (!$schedule) {
return response()->json(['msg'=>'Error not found','code'=>'404']);
}
foreach($schedules as $schedule){
$datax = [];
foreach($schedule->events as $event){
$datax[] = [
'id'=>$event->id,
'time'=>$event->time,
'category'=>$event->category,
'type'=>$event->title,
'designer'=>$event->designer,
];
}
$jadwal[] = [
'id'=>$item->id,
'date'=>$item->date,
'place'=>$item->stage,
'data'=>$datax,
];
}
return response()->json($jadwal);
}
but above solution is not recommended because send many request to server in any foreach loop, following solution is better:
public function getschedule(){
$schedules = Stage::with('events')->get(); // only this difference with above soloution and the rest is the same
if (!$schedule) {
return response()->json(['msg'=>'Error not found','code'=>'404']);
}
foreach($schedules as $schedule){
$datax = [];
foreach($schedule->events as $event){
$datax[] = [
'id'=>$event->id,
'time'=>$event->time,
'category'=>$event->category,
'type'=>$event->title,
'designer'=>$event->designer,
];
}
$jadwal[] = [
'id'=>$item->id,
'date'=>$item->date,
'place'=>$item->stage,
'data'=>$datax,
];
}
return response()->json($jadwal);
}

Yii2 - dynamically switch rules set in model

I want to dynamically substitute rules in model according to switch value on form.
In view:
<?php
$form = ActiveForm::begin([
'enableAjaxValidation' => true,
'validationUrl' => Url::toRoute('anounce/validation')
]);
?>
In controller:
public function actionValidation()
{
$model = new Anounce();
if (Yii::$app->request->isAjax && $model->load(Yii::$app->
request->post())) {
Yii::$app->response->format = 'json';
return ActiveForm::validate($model);
}
}
Excerpts from model:
class Anounce extends \yii\db\ActiveRecord
{
private $currentRuleSet; // Current validation set
// Here are arrays of rules with assignment
private $fullRuleSet; // = [...];
private $shortRuleSet; // = [...];
private $minRuleSet; // = [...];
public function init()
{
parent::init();
$this->currentRuleSet = $this->fullRuleSet;
}
public function rules()
{
return $this->currentRuleSet;
}
public function beforeValidate()
{
if ($this->idanounce_type === self::FULL) {
$this->currentRuleSet = $this->fullRuleSet;
} else if ($this->idanounce_type === self::SHORTER) {
$this->currentRuleSet = $this->shortRuleSet;
} else if ($this->idanounce_type === self::MINIMAL) {
$this->currentRuleSet = $this->minRuleSet;
}
return parent::beforeValidate();
}
}
Variable idanounce_type is a switch between rules.
Unfortunately, validation made according to full rules set (or rules set used in init), despite on which *RuleSet value assigned to currentRuleSet.
How to write dynamic switching of rules?
What you want here is to change validation according to the user's input. You can do this by defining scenarios in your model.
So firstly set scenarios where you put in it the fields that are to be validated. Example if you have username, password, and email fields, and you defined two scenarios, in SCENARIO_FIRST only username and password will get validated.
public function scenarios()
{
return [
self::SCENARIO_FIRST => ['username', 'password'],
self::SCENARIO_SECOND => ['username', 'email', 'password'],
];
}
Then in your controller, set the scenario according to the input:
public function actionValidation()
{
$model = new Anounce();
//example
if($condition == true)
{
$model->scenario = Anounce::SCENARIO_FIRST;
}
if (Yii::$app->request->isAjax && $model->load(Yii::$app->
request->post())) {
Yii::$app->response->format = 'json';
return ActiveForm::validate($model);
}
}
Read more about scenarios here and how to use them with validation here:
http://www.yiiframework.com/doc-2.0/guide-structure-models.html#scenarios

Laravel profile edit

Alright , I have used this way to save the users info and It works perfect,
static public function memberSave($request) {
$signup = false;
$member = new Members();
$member->name = $request['name'];
$member->email = $request['email'];
$member->password = bcrypt($request['password']);
$member->save();
if (!empty($member->id)) {
$new_id = $member->id;
DB::insert("INSERT INTO roles VALUES ($new_id, 5613)");
$signup = true;
Session::flash('sm', 'Thank you! You have signed up successfully!');
}
return $signup;
}
but when making this for editing the profile(by user) It doesn't work
becuase I use new(); (making object)
I also didn't succeed to use find(); so I tried to use this
static public function saveProfile($id,$name,$email,$password) {
$sql = "UPDATE members SET name=?,email=?,password=? WHERE id=?";
$member = DB::select($sql, [$name,$email,$password,$id]);
but when I want to bcrypt the password in laravel doesnt work .
this is the code also in the second page
public function postProfile(ProfileValidation $request) {
if (Members::saveProfile($request['id'], $request['name'], $request['email'], $request['password'])) {
return redirect('');
}
}
I hope getting helped for editing the users profile by laravel , thanks.
Your Members class must extend Eloquent\Model for following this code to work.
class Members extends Model {
// optional
protected $table = 'members';
...
To find and update the member using email,
// find the single member
$member = Members::where('email', request['email'])->first();
// update the member
$member->name = $request['name'];
$member->password = $request['password'];
// now save the updated member
$member->save();
In order to to encrypt Password, Laravel provides Hash Facade,
// import this
use Hash;
...
// encrypt Password
$encrypted = Hash::make($request['password']);
...
if you want your user automatically hash the password at your model put:
public function setPasswordAttribute($value)
{
$this->attributes['password'] = Hash::make($value);
}
and you can directly check for the user if exist create new or update it:
public function saveMember($request)
{
$member = Member::findOrNew($request->email);
//All your input you want to save
$member->save();
}

Resources