MSSQL Driver Issue - Eloquent save tries to set identity column on update - laravel

Am getting this "Cannot update identity column 'id'" bug when I try to update using eloquent.
Am using Laravel 5.2
$m = new ModelName;
$m->name = 'Test model insert';
$m->save();
$m1 = ModelName::find( $m->id );
if ($m1) {
$m1->name = 'Test model update';
$m1->save(); // Error occured here.
}

You can use
trait WithoutUpdateId
{
/**
* Perform a model update operation.
*
* #param \Illuminate\Database\Eloquent\Builder $query
*
* #return bool
*/
protected function performUpdate(Builder $query)
{
// If the updating event returns false, we will cancel the update operation so
// developers can hook Validation systems into their models and cancel this
// operation if the model does not pass validation. Otherwise, we update.
if ($this->fireModelEvent('updating') === false) {
return false;
}
// First we need to create a fresh query instance and touch the creation and
// update timestamp on the model which are maintained by us for developer
// convenience. Then we will just continue saving the model instances.
if ($this->usesTimestamps()) {
$this->updateTimestamps();
}
// Once we have run the update operation, we will fire the "updated" event for
// this model instance. This will allow developers to hook into these after
// models are updated, giving them a chance to do any special processing.
$dirty = $this->getDirty();
if (count($dirty) > 0) {
unset($dirty['id']);
$this->setKeysForSaveQuery($query)->update($dirty);
$this->fireModelEvent('updated', false);
$this->syncChanges();
}
return true;
}
}
I have deleted id here
unset($dirty['id']);
In Model class
class ModelName extends Model
{
use WithoutUpdateId;
...
}

I figured my problem. I did unset $m1->id before calling $m1->save(). And it works great now.

Related

Magento 2.3 graphql get custom attribute option values

I’m pretty frustrated with the graphql api of Magento. How is it not possible to receive the values of a custom attribute. I mean it’s easy to make a custom select-option attribute and making it available through graphql but you are only getting the integer value.
What’s the best procedure to following
1. Query the complete attribute to get the value
2. Extending the graphql schema (this involves a developer every time the client changes something)
3. Am I missing some functionality inside the graphql endpoints to Fix this?
From Native GraqpQl we cannot get the expected result.But we can write a custom module with the graphql and achieve it.What my suggestion is get the product detail by sku using _productRepository->get($sku);. This will return the entire product details.So you can get the attribute data by using $attributes = $_product->getAttributes(); Here is my data provider file.
/**
* #params string $sku
* this function return all the product data by product sku
**/
public function getProductBySku($sku)
{
return $this->_productRepository->get($sku);
}
/**
* #params int $id
* this function return all the word of the day by id
**/
public function getAttributesBySku( $sku ){
$_product = $this->getProductBySku($sku);
$attributes = $_product->getAttributes();// All Product Attributes
$attributes_data = [];
$x=0;
foreach ($attributes as $attribute) {
if($attribute->getIsUserDefined()){ // Removed the system product attribute by checking the current attribute is user created
$attributeLabel = $attribute->getFrontend()->getLabel();
$attributeValue = $attribute->getFrontend()->getValue($_product);
if($attribute->getAttributeCode()=="language"){
$attributeLabelAndValue = $attributeLabel." - ".$attributeValue;
$attributes_data[$x]['atr_data'] = $attributeLabelAndValue;
}
}
$x++;
}
return $attributes_data;
}
This code will return the need
$attribute->getFrontend()->getLabel(); this will return the label and
$attribute->getFrontend()->getValue($_product); this will return the value.
To check the entire graphQl query and resolver file kindly view How to get product attribute value, label using GraphQl in Magento 2.3?
Hope this will help you.
You can try creating custom graphql attribute with custom resolver.
$product = $this->_productRepository->getById($value['entity_id']);
$data = array();
foreach ($args['fields'] as $fi) {
$att = $product->getResource()->getAttribute($fi);
if (isset($att) && $att) {
if (in_array(
$att->getFrontendInput(),
['multiselect', 'select']
)) {
$data[$fi . '_label'] = $product->getAttributeText($fi);
}
$data[$fi] = $product->getData($fi);
}
}
return json_encode((object) $data);
which should display all provided attributes with their labels.
"sku": "testProduct",
"fabric": 60,
"work": 65,
"dynamicAttributes": "{
"fabric_label":"Pure Silk",
"fabric":"60",
"work_label":"Tanchoi",
"work":"65"
}",
Check out entire module here

How to Override Update Method in Laravel 5.8

I'm trying to create a ticketing system that's linked to timesheets. Whenever someone updates a ticket, they have the option of submitting how much time has been spent on it, in a time_spent form object. Timesheets are polymorphically linked to many objects.
I want to create a trait, CreatesTimesheets, then apply that to relevant models so that:
Each of those models gets a timesheets() function.
It overrides the update() method of each model that it's a trait of, to check whether any time was submitted in time_spent.
It's the second bit that isn't working. My code is as below, and when I update the model (which works fine), this code doesn't fire at all, even testing it with a simple dd().
How do I fix this?
<?php
namespace App\Traits;
use App\Models\HR\Timesheet;
use Auth;
trait CreatesTimesheets
{
public function update(array $attributes = [], array $options = [])
{
dd('test');
if ($request->time_spent)
{
$timesheet = new Timesheet;
$timesheet->time_logged_in_mins = $request->time_spent;
$timesheet->appointment_id = Auth::user()->appointedJobIDToUse();
$this->timesheets()->save($timesheet);
}
parent::update($attributes, $options);
}
public function timesheets()
{
return $this->morphToMany('App\Models\HR\Timesheet', 'timesheetable');
}
}

Return result after the event is fired in Laravel 5.2

My original code is below
/*
Create the Role
*/
$result = (new RoleDb())->Create($obj);
if($result["Success"]) {
/*
| Get all Modules
*/
$Permissions = $this->Module->All($obj->RoleID);
$list = [];
/*
| Prepare the list that will be assigned to Newly created role.
*/
foreach($Permissions["Data"] as $Permission) {
$RolePermissionOM = new RolePermissionOM();
$RolePermissionOM->PermissionID = $Permission->PermissionID;
$RolePermissionOM->IsActive = $Permission->DefaultPermission;
$RolePermissionOM->RoleID = $result["Data"];
array_push($list, $RolePermissionOM);
}
/*
| Create default permissions for above created role.
*/
return $this->RolePermission->CreateDefaultPermissions($list, $result["Data"]);
}
Now, in my application, there are 3 more points where role is being created and instead of code duplication, I though to convert this code into event. SO whenever a role is being created, an Event is being fired to create the permission records for that role.I wrote the below code.
Event::fire(new RoleCreationEvent($result));
// `$result` contains the newly created RoleID.
Question : In my original code, I was able to get the result to check if the permissions are saved correctly or not. How will I do that in case of firing the Event ?
Event Code
class RolePermissionEvent extends Event
{
use SerializesModels;
public function __construct($RoleID, $Module, $RolePermission)
{
$Permissions = $Module->All($RoleID);
$list = [];
foreach($Permissions["Data"] as $Permission) {
$RolePermissionOM = new RolePermissionOM();
$RolePermissionOM->PermissionID = $Permission->PermissionID;
$RolePermissionOM->RoleID = $RoleID;
array_push($list, $RolePermissionOM);
}
return $RolePermission->CreateDefaultPermissions($list, $RoleID);
}
}
Create a listener like it described here https://laravel.com/docs/5.2/events.
Move all your code from constructor to the listener.
Pass an even to the listener that allows it to use event data.
Return 'return $RolePermission->CreateDefaultPermissions($list, $RoleID);' from listener.
Events shouldn't return a value, they are just reacting to something that happened. Something like sending an email after a new user was registered.
In your case, use eloquent model events instead of domain events. Also, as an alternative, you could use a service class.

Laravel 5.2 Event Testing: expectsEvent not seeing the event fired although it is being fired

I have been trying to test events and I had it working yesterday. That was before I started to refactor the test code to keep it from being too repetitious. I added the setUp method call to generate the fake data using ModelFactories. This was done in each test case yesterday and as stated it was working.
I am thinking it has something to do with using the setUp method but I have no idea why that would be the case. First I attempted to use setUpBeforeClass() static method as that is only run once on unit test runs. However the laravel application is not actually setup until the first call to setUp()... A possible bug maybe? It is documented in this SO post Setting up PHPUnit tests in Laravel.
Therefore I opted to use the setUp method and just check to see if the static property is null or not, if it is null then it generates the data, if not it just goes on it's way.
Here is the output from running phpunit on the project
➜ project git:(laravel-5.2-testing) ✗ phpunit
PHPUnit 5.2.10 by Sebastian Bergmann and contributors.
E 1 / 1 (100%)
Time: 8.94 seconds, Memory: 33.50Mb
There was 1 error:
1) UserEmailNotificationsTest::testNotificationSentOnGroupMediaSaving
Exception: These expected events were not fired: [\App\Events\GroupMediaSaving]
/Users/jcrawford/Dropbox/Work/Viddler/Repositories/l5_media_communities/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/MocksApplicationServices.php:44
/Users/jcrawford/Dropbox/Work/Viddler/Repositories/l5_media_communities/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestCase.php:127
FAILURES!
Tests: 1, Assertions: 0, Errors: 1, Skipped: 8.
Here is my code for the unit test file that I have created.
<?php
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class UserEmailNotificationsTest extends \TestCase
{
use DatabaseTransactions;
const COMMUNITIES_TO_CREATE = 3;
const USERS_TO_CREATE = 10;
const ADMINS_TO_CREATE = 5;
protected static $communities = null;
protected static $users = null;
protected static $admins = null;
public function setUp()
{
parent::setUp(); // TODO: Change the autogenerated stub
if(is_null(self::$communities)) {
self::$communities = factory(\Community::class, self::COMMUNITIES_TO_CREATE)->create()->each(function ($community) {
self::$users[$community->id] = factory(User::class, self::USERS_TO_CREATE)->create()->each(function (\User $user) use ($community) {
$user->community()->associate($community);
$user->save();
});
self::$admins[$community->id] = factory(User::class, self::ADMINS_TO_CREATE, 'superadmin')->create()->each(function (\User $admin) use ($community) {
$admin->community()->associate($community);
$admin->save();
});
$community->save();
});
}
}
public static function getRandomCommunityWithAssociatedData()
{
$community = self::$communities[mt_rand(0, count(self::$communities)-1)];
return ['community' => $community, 'users' => self::$users[$community->id], 'admins' => self::$admins[$community->id]];
}
/**
* Test that the notification event is fired when a group media
* item is saved.
*/
public function testNotificationSentOnGroupMediaSaving()
{
$data = self::getRandomCommunityWithAssociatedData();
// FOR SOME REASON THIS SAYS THE EVENT IS NEVER FIRED WHEN IT ACTUALLY IS FIRED.
$this->expectsEvents(['\App\Events\GroupMediaSaving']);
$community = $data['community'];
$admin = $data['admins'][0];
$user = $data['users'][0];
$asset = factory(Asset\Video::class)->make();
$asset->community()->associate($community);
$asset->user()->associate($admin);
$asset->save();
$group = factory(Group::class)->make();
$group->community()->associate($community);
$group->created_by = $admin->id;
$group->save();
$groupMedia = factory(GroupMedia::class)->make();
$groupMedia->asset()->associate($asset);
$groupMedia->user()->associate($user);
$groupMedia->group()->associate($group);
$groupMedia->published_date = date('Y-m-d H:i:s', strtotime('-1 day'));
$groupMedia->save();
// I can print_r($groupMedia) here and it does have an ID attribute so it was saved, I also put some debugging in the event object and it is actually fired.....
}
}
Any thoughts as to why it doesn't see the events being fired? I find it odd that they are fired if I create the models inside the test case but seem to be failing when done inside of setUp(). The worst part is I am not creating the GroupMedia model in the setUp method rather that is done in the test case.
I have also dumped the data that is returned from the getRandomCommunityWithAssociatedData method and it is returning proper model objects all with id attributes which tells me they were all saved to the database during creation.
As requested here is the code that is actually firing the event, it is located in the GroupMedia model in the static boot method.
protected static function boot()
{
parent::boot();
static::saving(function($groupMedia) {
Event::fire(new \App\Events\GroupMediaSaving($groupMedia));
});
}
If you look at the source code for expectsEvents (inside the trait Illuminate/Foundation/Testing/Concerns/MocksApplicationServices), you will see that it calls the function withoutEvents, which mocks the application event dispatcher, suppressing and collecting all future events.
The problem for you is that the setUp function will have already been called at this point, so your events will not be caught and logged by the test, and will not show up when the assertion is evaluated.
In order to correctly see the events firing, you should make sure that you declare the assertion before the code which triggers the events.
Same thing happened to me, $this->expectsEvent() was not detecting that the event was being fired or prevent it from propagating to the event listeners..
Previously, I was firing my events using Event::fire(new Event()). I tried changing it to event(new Event()) and the test suddenly works correctly now, with it detecting that the event has been fired and silencing the event.

Doctrine Behavior / Templates: Is there a way overwrite or load data into the invoking Doctrine_Record from a Doctrine_Template?

Hy!
I'm new to the Doctrine library. I'm trying to take advantage of the Doctrine Behavior (Templating) system to build a Authenticable behavior but I ran into a few problems.
This is how i wanted to declare it:
class BaseAdminUser extends Doctrine_Record{
public function setTableDefinition(){
// ...
}
public function setUp(){
// ...
$this->actAs(new Doctrine_Template_Authenticatable());
}
}
This is how i wanted to use it:
In my admin bootstrap file:
// ...
$admin = new AdminUser();
if(!$admin->login_check()){
redirect("login.php");
}else{
// $admin->id is available
}
In my admin login.php page:
// ...
if($data = $_POST){
$admin->login($data);
}
I wanted my template to use Native PHP Sessions (using $_SESSION) to do the following:
upon login set a session and save the record Doctrine_Core::HYDRATE_ARRAY in it
upon instantiation of Record and Template check if the session data exists
and load the invoking Record with the id from the session
One approach i tried was to re-route the $invoker. That doesn't work.
class Doctrine_Template_Authenticatable extends Doctrine_Template{
// ...
public function setUp(){
// ...
if(isset($session_data['id'])){
$invoker = &$this->getInvoker();
$invoker = $this->getTable()->find($session_data['id']);
}
}
// ...
}
I've looked trough the Doctrine_Record API Documentation and i could not find a function which would load (hydrate) a different table row into a object based on id or otherwise.
There's fromArray($arr) and hydrate($arr, $overwrite):
Doctrine_Record::fromArray(string array, bool deep))
Doctrine_Record::hydrate(array data, boolean overwriteLocalChanges)
But that is fake and would just load the data without the id.
$this->getInvoker()->hydrate($session_data);
// (...)
echo $admin->id; // empty
$this->getInvoker()->fromArray($session_data);
// Uncaught exception 'Doctrine_Table_Exception' with message 'Unknown relation alias id'
My question is:
Is there a way overwrite or load data into the invoking Doctrine_Record from a Doctrine_Template ?

Resources