Laravel get relationship model within object after save() - laravel

I am using laravel 4.2.
I have two models as below :
class User extends Eloquent{
protected $table = 'users';
public function user_card_details(){
return $this->hasMany('User_card_details');
}
}
And
class User_card_details extends Eloquent {
protected $table = 'user_card_details';
public $timestamps = true;
public $softdeletes = true;
public function user(){
return $this->belongsTo('User')->first();
}
}
And I can save the relationship record using :
$user_card_details = new User_card_details();
$user_card_details->card_number = Input::get('card_number');
$user_card_details->card_exp_month = Input::get('card_expires_m');
$user_card_details->card_exp_year = Input::get('card_expires_y');
$user_card_details->card_cvv = Input::get('card_cvv');
$user->user_card_details()->save($user_card_details);
Up to this it works fine for me.
After save() , I want the user object should be populated with user_details.
So if I want to use the properties, I can use it like :
echo $user->user_card_details->card_number;
But it is not working now.
Any suggestions?
Thanks

You have to remove the () to get the actual model or collection:
echo $user->user_card_details->card_number;
When you're calling the actual function, you'll receive an instance of the Query builder.
Also, it seems that you're not persisting your $user_card_details-object before you try to bind it to your user:
$user_card_details = new User_card_details();
$user_card_details->card_number = Input::get('card_number');
$user_card_details->card_exp_month = Input::get('card_expires_m');
$user_card_details->card_exp_year = Input::get('card_expires_y');
$user_card_details->card_cvv = Input::get('card_cvv');
$user_card_details->save(); //Added this line.
$user->user_card_details()->save($user_card_details);
The more correct way would be:
$user_card_details = [
'card_number' => Input::get( 'card_number' ),
'card_exp_month' => Input::get( 'card_expires_m' ),
'card_exp_year' => Input::get( 'card_expires_y' ),
'card_cvv' => Input::get( 'card_cvv' ),
];
$userCardDetailObj = $user->user_card_details()->create( $user_card_details );
Now, your User_card_detail-instance will be available as the returned object.

Related

Laravel Eloquent relationship object stale even though data correct in database

I'm using Laravel 5.7 and have a one-to-one relationship between 2 eloquent models.
I have this simple function that works well, and the correct values persist to the database:
public function saveMarketingOriginInfo(Contact $contact, $data) {
$contact->marketingOrigin()->create($data);
$this->makeOtherChangesByReference($contact->marketingOrigin);
$contact->marketingOrigin->save();
return $contact->marketingOrigin;
}
However, when writing a functional test for it, I noticed that the object that it returns is stale (doesn't have the correct values in its properties).
My tests only pass if I change the return statement to return \App\Models\MarketingOrigin::find($contact->id);.
(MarketingOrigin uses 'contact_id' as primary key.)
What am I doing wrong?
How can I return the same object that was just saved in the previous line ($contact->marketingOrigin->save();) without making a database read query (find())?
Update to respond to comments:
protected $table = 'marketing_origins';//MarketingOrigin class
protected $primaryKey = 'contact_id';
protected $guarded = [];
public function contact() {
return $this->belongsTo('App\Models\Contact');
}
The test:
public function testSaveMarketingOriginInfo() {
$helper = new \App\Helpers\SignupHelper();
$contactId = 92934;
$contact = factory(\App\Models\Contact::class)->create(['id' => $contactId]);
$leadMagnetType = 'LMT';
$audience = 'a60907';
$hiddenMktgFields = [
'audience' => $audience,
'leadMagnetType' => $leadMagnetType
];
$result = $helper->saveMarketingOriginInfo($contact, $hiddenMktgFields);
$this->assertEquals($result->contact_id, $contactId, 'contact_id did not get saved');
$this->assertEquals($result->campaignId, '6075626793661');
$this->assertEquals($result->leadMagnetType, $leadMagnetType);
$marketingOrigin = \App\Models\MarketingOrigin::findOrFail($contactId);
$this->assertEquals($marketingOrigin->adsetId, '6088011244061');
$this->assertEquals($marketingOrigin->audience, $audience);
$this->assertEquals($marketingOrigin, $result, 'This is the assertion that fails; some properties of the object are stale');
}
This is because the relationship has not been loaded yet.
You could try $contact->load('marketingOrigin'); to eager load the relationship:
public function saveMarketingOriginInfo(Contact $contact, $data) {
$contact->marketingOrigin()->create($data);
$this->makeOtherChangesByReference($contact->marketingOrigin);
$contact->marketingOrigin->save();
$contact->load('marketingOrigin'); // <---- eager load the relationship
return $contact->marketingOrigin;
}

Proper syntax for Laravel Eloquent insert/update

I have seemingly simple problem that I still can't solve after days of browsing around.
Say I have Debitur model.
class Debitur extends Model
{
protected $table = 'debiturs';
protected $fillable = [
'name', 'address',
];
public function debiturWife(){
return $this->hasOne('App\DebiturWife');
}
}
And I have a DebiturWife model.
class DebiturWife extends Model
{
protected $table = 'debitur_wifes';
protected $fillable = [
'nama', 'address', 'photo',
];
public function debitur(){
return $this->belongsTo('App\Debitur');
}
}
I can get Debitur and DebiturWife data in the DebiturController like this:
return Debitur::with('debiturWife')->find($debitur_id);
Now I want to create a new Debitur and DebiturWife, how do I do that?
Something like
$debitur = new Debitur;
$debitur->name = $request->name;
$debitur_wife = new Debitur.DebiturWife; //obviously doesn't work
$debitur = new Debitur;
$debitur->name = $request->name;
$debitur->save();
$debitur_wife = new DebiturWife([
'name' => 'Foo'
]);
$debitur->debiturWife()->save($debitur_wife);
Documentation
What if later I want to update DebiturWife based on Debitur's id?
$debitur = Debitur::with('debiturWife')->find($id);
$debitur->debiturWife->name = 'new name';
$debitur->debiturWife->address = 'new address';
$debitur->save();
You could do this way:
$debitur = new Debitur;
$debitur->name = $request->name;
$debitur->save();
$debitur->debiturWife()->create([
'name' => $request->name,
'address' => $request->address,
'photo' => $request->photo
]);
Another way of doing this would be —
$debiturWife = new DebiturWife();
$debiturWife->name = $request->name;
$debiturWife->address = $request->address;
$debiturWife->photo = $request->photo;
$debitur->debiturWife()->save($debiturWife);
The debitur_id will be automatically set. The only difference between the create & save method is that the create method accepts a plain PHP array whereas the save method accepts an instance of DebitureWife
See the documentation for better understanding.

Saving Model data to database

I have a Report Model which is like the following.
class Report extends Model
{
protected $table = 'reports';
protected $guarded = [];
public function leadsCollection()
{
return $this->hasMany('App\ReportModels\LeadsCollection');
}
}
A Report can have many LeadsCollection, its Model is the following.
class LeadsCollection extends Model
{
protected $table = 'leadsCollection';
protected $guarded = [];
private $xmlElement;
public function __construct($xmlElement = null, $attributes = array()) {
parent::__construct($attributes);
$this->xmlElement = $xmlElement;
}
public function report()
{
return $this->belongsTo('App\ReportModels\Report');
}
function asArray(){
$reportItem = array();
foreach($this->xmlElement->Leads->Lead as $lead) {
$dateIdentified = date("d/m/Y", strtotime($lead->Date));
$reportItem[] = array(
'LeadID' => (string)$lead->ID,
'Client' => (string)$lead->Client->Name,
'Category' => (string)$lead->Category,
'DateIdentified' => $dateIdentified,
'LeadName' => (string)$lead->Name,
'Owner' => (string)$lead->Owner->Name
);
}
return $reportItem;
}
}
Now I am trying to save some data to a database. So I get a list of all Leads by calling my LeadsCollection and passing it an XML list of Leads.
I then loop these Leads and add it to an array. At the same time however I need to save it to the database. This is what I have so far.
public function getForecastReportForLeads() {
$leads = new LeadsCollection(new \SimpleXMLElement(Helper::getCurrentLeads()));
$reportArray = array();
foreach ($leads->asArray() as $lead) {
$report = new Report();
$report->reportName = 'Lead Forecast';
if($report->save()) {
$leads->leadId = $lead['LeadID'];
$leads->leadCategory = $lead['Category'];
$leads->dateIdentified = $lead['DateIdentified'];
$leads->leadName = $lead['LeadName'];
$leads->owner = $lead['Owner'];
$leads->client = $lead['Client'];
$leads->report_id = $report->id;
$leads->save();
$reportItem = array(
'leadData' => $lead
);
$reportArray[] = $reportItem;
}
}
return $reportArray;
}
So I create the Report item, and within the database if I have 7 Leads I end up with 7 Report rows within my reports table, as it should be. However, when I save the Leads, I only end up with 1 row in my leadsCollection table, every other entry seems to be overridden. I think this is because I am not creating the Lead Object within the loop. However, I cant really create it within the loop because I need to loop whats returned when I first create it.
Not sure how clear I am but is there anything I can add to my Model so I can stop any overriding? Or do I need to do this another way?
Thanks
Either you get the variable inside the save method or initialize the new
$report = new Report($reportItem);
$report->save($report)
I'm having a similar Issue right, let me show my code. It would work for your case. My bug is that I'm updating and the plan_detail.id gets moved instead of creating a new one. But if you create would be fine:
public function store(Request $request)
{
$this->validate($request, [ 'title' => 'required',
'description' => 'required']);
$input = $request->all();
$plan_details = Plan_Detail::ofUser()->get();
$plan = new Plan($request->all());
DB::beginTransaction();
Auth::user()->plans()->save($plan);
try {
foreach ($plan_details as $k => $plan_detail)
Plan::find($plan['id'])->details()->save($plan_detail);
DB::commit();
} catch (Exception $e) {
Log::error("PGSQL plan detail " . $e->message());
DB::rollback();
session()->flash('message', 'Error al guardar el plan de entreno');
}
return redirect('plans');
}

ZF2 transalation is not working in form class

I am using zendframework 2 and My translations are not working here in form class where the form is formed and there is validation, elsewhere in whole applications they are working properly.
I have pasted all the code in my file with namespaces.
<?php
namespace Services\Form;
use Zend\Form\Form;
use Zend\Form\Element;
use Zend\InputFilter\Input;
use Zend\InputFilter\InputFilter;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class ProfilePicForm extends Form implements ServiceLocatorAwareInterface
{
protected $serviceLocator;
public function setServiceLocator(ServiceLocatorInterface $sl)
{
$this->serviceLocator = $sl;
}
public function getServiceLocator()
{
return $this->serviceLocator;
}
public function init()
{
$routeMatch = $this->getServiceLocator()->getServiceLocator()->get('Application')->getMvcEvent()->getRouteMatch();
$translator = $this->getServiceLocator()->getServiceLocator()->get('viewHelperManager')->get('translate')->getTranslator();
$action = $routeMatch->getParam('action');
// Form
parent::__construct('profile_pic_form');
$this->setAttribute('method', 'post');
$this->setAttribute('enctype','multipart/form-data');
$profile_pic_form_csrf = new Element\Csrf('profile_pic_form_csrf');
$profile_pic_form_csrf->setCsrfValidatorOptions(array('timeout'=>'3600'));
$this->add($profile_pic_form_csrf);
$profile_pic = new Element\File('profile_pic');
$this->add($profile_pic);
// Validation
$inputFilter = new InputFilter();
$profile_pic = new Input('profile_pic');
$profile_pic->getFilterChain()
->attach(new \Lib\MyLib\Filter\RenameUpload(array(
'target' => SERVICE_PROFILE_PIC_UPLOAD_PATH.'/profile-pic.*',
'use_upload_extension' => true,
'randomize' => true
)));
$required = true;
$profile_pic->setRequired($required);
$validator = new \Zend\Validator\File\UploadFile();
$validator->setOptions(array(
'messageTemplates' => array(
\Zend\Validator\File\UploadFile::FILE_NOT_FOUND => 'Please select picture.'
)));
$profile_pic->getValidatorChain()->attach($validator,true);
$validator = new \Zend\Validator\File\Size(array('max' => 250*1024));
$validator->setMessage(**$translator->translate('MyAccountPictureErrorMessage1')**);
$profile_pic->getValidatorChain()->attach($validator,true);
$validator = new \Zend\Validator\File\Extension('png,jpg');
$validator->setMessage(**$translator->translate('MyAccountPictureErrorMessage2')**);
$profile_pic->getValidatorChain()->attach($validator,true);
$inputFilter->add($profile_pic);
$this->setInputFilter($inputFilter);
}
this is my controller function.
public function profileAction() {
$this->layout('ajax-layout');
$var = new \stdClass();
$viewmodel = new ViewModel();
$this->authPlugin()->checkLogin();
$this->servicePlugin()->checkSid();
$this->layout()->setVariable('allowedFeatures', $this->featurePlugin()->getAllowedFeatures());
$this->languagePlugin()->translate();
$var->userInfo = $this->authPlugin()->getUserInfo();
if($this->params()->fromRoute('sid') !== null){
$var->sid = $this->params()->fromRoute('sid');
}
elseif ($this->params()->fromRoute('id') !== null) {
$var->sid = $this->params()->fromRoute('id');
}
// ----------------------- i m here --------------------------
// $var->sid = $this->params()->fromRoute('sid');
$var->profilePicForm = $this->getServiceLocator()->get('FormElementManager')->get('\Services\Form\ProfilePicForm');
$var->serviceNameForm = $this->getServiceLocator()->get('FormElementManager')->get('\Services\Form\ServiceNameForm');
$var->service = $this->getServices()->fetchServiceById($var->sid);
// Fetch payment history
$var->paymentHistory = $this->getServiceLocator()->get('Services\Model\PaymentTransactionService')->fetchPaymentTransactionsByServiceId($var->sid);
$var->timezones = $this->getTimeZoneTable()->listAll();
$viewmodel->setVariables(array('var' => $var));
return $viewmodel;
}
This is happening because of your validator.
I already talked about this problem, when you call new validators without the service locator :
https://stackoverflow.com/a/36500438/3333246
To fix that you need to set the translator in your options because:
class Size extends AbstractValidator
abstract class AbstractValidator implements
Translator\TranslatorAwareInterface,
ValidatorInterface
And TranslatorAwareInterface is not initialized if you instanciate a new Validator without ServiceLocator.
So your validators need options declared like this in your code:
$validator = new \Zend\Validator\File\Size(array('translator' => $translator, 'max' => 250*1024));
$validator->setMessage('MyAccountPictureErrorMessage1');
No need to translate the message now, the validator will translate it for you.
For my comment, about your code, nevermind it's not related to your problem. It's just personal in fact.
EDIT:
You don't need this translator :
$translator = $this->getServiceLocator()->getServiceLocator()->get('viewHelperManager')->get('translate')->getTranslator();
But this one
$translator = $this->getServiceLocator()->get('translator');
I have found another way to do this job, i have made an ajax call and on its response i show the divs having the translations.

Setting a table name in a model?

Im trying to pass in a table name to my model, as the model operates on two tables, but has the same methods.
I do it like so:
$this->model = new Emotions(array('section' => 'red'));
And in the model I set the table like:
public function __construct($attributes = array(), $exists = false){
parent::__construct($attributes, $exists);
$this->table = $attributes['section'];
}
But I get the error:
Undefined index: section
Any ideas where I'm going wrong?
Yes i get it, This class maybe running twice.
Please try this.
public function __construct($attributes = array(), $exists = false){
parent::__construct($attributes, $exists);
if(isset($attributes['section'])) {
$this->table = $attributes['section'];
}
}
My personal suggestion
<?php
class Emotions extends Eloquent
{
public function setTableName($name)
{
$this->table = $name;
return $this;
}
}
And you can use like this
$emotion = new Emotions(array('foo' => 'bar'))
->setTableName('blabla')
->save();
add below line to your class.
protected $fillable = array('section');
http://laravel.com/docs/eloquent#mass-assignment

Resources