I have this function that receives a "user" model by parameter , I want to collect the properties of that object, for this I do it this way, the code works for me but the editor "phpstorm" complained to me with this error and it was to know what would be the best way to do this.
Thank you
public function sendEmail(User $user)
{
$params = [
'name' => "{$user->completeName}",
'language' => "{$user->locale}",
'user_id' => "{$user->id}"
];
}
Field accessed via magic method less... (Ctrl+F1)
Inspection info: Referenced field is not found in subject class. Note: Check is not performed on objects of type "stdClass" or derived.
Thanxs,
maybe this is simpler
$params = $user->toArray();
or
$params = $user->toJson();
That's because in Laravel your model does not actually have the properties defined.
In PHP there is the concept of magic methods though ( http://php.net/manual/en/language.oop5.overloading.php#object.get ), where the __get() method allows you to basically intercept the access to an otherwise inaccessible (or non-existing) property.
This is what happens behind the scenes in Laravel. All your property accesses are "intercepted" and Laravel looks if your database contains a column which is named like the property you are trying to access (very simplified speaking).
In a Laravel context you can savely ignore this warning.
Related
I have an object that stores another subObject in it's data attribute. The subObject in the data attribute can be called vie a magic method like this:
$object->subObject. The subObject also has a data attribute in which it's value is stored. Currently when I do $object->subObject it automatically returns the value stored in the data attribute of the subObject. So basically the whole structure looks somewhat like this:
object [
data => subObject [
data => 'value'
]
]
$object->subObject //returns 'value'
I hope that this is understandable so far. Now I want to have the ability to execute someFunc() on the subObject: $object->subObject->someFunc(). That is generally not a problem. But I also want that, if I don't call a method on the subObject at all, the value of it's data attribute is returned ('value'). Is it possible to figure out within an object/a class if a method is called on it or not and act accordingly?
This is not possible. When you access a value, you get the same result regardless of what is going to be done with that value. It can't be different depending on whether you're going to call another method on it.
Remember that
$object->subObject->someFunc();
is essentially equivalent to
$temp = $object->subObject;
$temp->someFunc();
If the $object->subObject returned 'value', the first line would set $temp = 'value', and then $temp->someFunc() wouldn't work.
My controller looks like this:
public function store(Request $request) {
$validateData = $request->validate([
'name' => 'required|unique:languages',
'code' => 'required|size:3',
'flag' => 'required|size:2'
]);
$language = new Language();
$language->name = $request->name;
$language->code = $request->code;
$language->flag = $request->flag;
$saveLanguage = $language->save();
if(!$saveLanguage){
return response()->json(['error'=>'Something went wrong, please try later.'],500);
}
return response()->json(['success'=>'Language has been created successfully', 'data'=>$language],200);
As you can see, I am instantiating a new Language object and everything works fine, but first problem is, imagine I change Language class in future (for example: you have to pass 2 parameters in constructor), I have to change this controller and every other controllers where I am instantiating Language object.
The second problem is I can't or it's too hard to test this controller.
I am curious what is the best solution to solve this problems in laravel?
For example is it a good solution to use simple factory or factory method pattern for every model I am using in my controllers.
I think when you write something like this $var = new SomeClass() in other class, this otherClass is depends on SomeClass and when you want to change SomeClass you have to update otherClass to. What do you think abaout this, how can I avoid this.
Or you could use the Eloquent Create function, this way you don't have to worry about the constructor.
$language = Language::create($request->only(['name', 'code', 'flag']));
This function will insert the data in the database and return the model.
Problem #1: If you change the signature of a constructor (e.g. adding new required parameters to the Language class constructor), then yes, you'll need to update all places where that constructor is called. There's no way around that, unless you can encapsulate the logic for what those parameters should be into a helper method somewhere (though you'll still need to update all calls the first time, while future changes will be abstracted away). However, modern IDEs (e.g. PHPStorm) can help you automate the process of replacing the old signature with the new signature.
Problem #2: You can actually test a controller like this quite easily. To take an example from the Laravel docs and apply it to your code, you could do something like this (in Laravel 7.x):
$response = $this->postJson('/language', ['name' => 'Swedish', 'code' => 'swe', 'flag => 'SE']);
$response
->assertStatus(200)
->assertJson([
'success' => 'Language has been created successfully',
])
->assertJsonPath('data.name', 'Swedish')
->assertJsonPath('data.code', 'swe')
->assertJsonPath('data.flag', 'SE');
I have an A entity and this have a property call B as relation 1:n from B to A. When I update A in TCA backend interface, when an particular field is active, the solution runs a hook of type function processDatamap_postProcessFieldArray (...)
I have to create a new element of B and save in ObjectStorage attribute of A. This works in execute time, create an objet and attaching it, but can not save in DB. I have tried with functions of extbase and Repository but it does not work. In my reserch identified the framework Doctrine for create queries, similar to persistence behavior, but in this case I could save the new object of B.
My question is: how could I use Doctrine for build query that allows to make update for object A, adding the new element B and save this element in the relation in DB.
I am working with TYPO3 7.6
You shouldn't use Extbase within the DataHandler hooks. Also plain database queries (neither with Dotrine or TYPO3_DB) are not good idea for creating entities within BE. Better way is to use TYPO3 DataHandler API. Example creation of Entity B during create/edit of Entity A could look like that.
Register hook typo3conf/ext/example/ext_localconf.php
defined('TYPO3_MODE') || die('Access denied.');
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass']['example'] = 'Vendor\\Example\\DataHandling\\DataHandler';
typo3conf/ext/example/Classes/DataHandling/DataHandler.php
namespace Vendor\Example\DataHandling;
use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Core\Utility\StringUtility;
class DataHandler implements SingletonInterface
{
public function processDatamap_afterDatabaseOperations(
string $status,
string $table,
$id,
$fieldArray,
\TYPO3\CMS\Core\DataHandling\DataHandler $dataHandler
) {
// Do nothing if other table is processed
if ($table !== 'tx_example_domain_model_entitya') {
return;
}
// Get real UID of entity A if A is new record
$idOfEntityA = $dataHandler->substNEWwithIDs[$id];
// Example fields of entity B
$entityB = [
'sys_language_uid' => '0',
'entitya' => $idOfEntityA,
'hidden' => '0',
'title' => 'I\'m entitty B',
'starttime' => '0',
'endtime' => '0',
'pid' => $fieldArray['pid'],
];
// Add entity B associated with entity A
$dataHandler->start(
[
'tx_example_domain_model_entityb' => [
StringUtility::getUniqueId('NEW') => $entityB
]
],
[]
);
$dataHandler->process_datamap();
}
}
Tested on 8.7, but will work on 7.6 too. Here you can read more about DataHandler https://docs.typo3.org/typo3cms/CoreApiReference/8.7/ApiOverview/Typo3CoreEngine/Database/
In contrary to the previous answer, I see no reason, why extbase shouldn`t be used in the DataHandler Hooks. I do it myself in an extension with dynamic objects that are being synchronized via a SOAP-Webservice.
You got to keep few things in mind (in this order inside the hooked function) :
-Obey naming policies !!
-Instantiate the ObjectManager manually via GeneralUtility::makeInstance
-Get ALL the repositories manually (with all I mean really all.. also repositories of childs of models you are working with inside the hooked function).
-Create new object instances with object manager => not with "new".
Then you can just add childs to parents as you are used to.. but dont forget to persistAll() via persistenceManager manually in the end.
Hope this could help. Basically, a function hooked via DataMap Hook acts like a static function called via ajax => you have to make sure to get all the desired utilities and managing classes manually, because typo3 doesn`t auto-inject them.
Hope this helps,
Oliver
Here's a test file:
class MyTest extends CDbTestCase
{
public $fixtures = array(
'my_data' => 'MyData',
);
public function testMyFunction()
{
$myObjectNotInDefaultScope = $this->my_data('out_of_scope_object');
//Can't do anything with $myObjectNotInDefaultScope since it returns null
// Is it possible to use resetScope?
// I can always set a primary key for the object and use findByPk but that's a hack
}
}
and here's the corresponding fixture:
<?php
return array(
'out_of_scope_object' => array(
'title' => 'This one is out of scope',
'status' => 'archived', // so not in the default scope
),
'in_scope_object' => array(
'title' => 'This one is in scope',
'status' => 'active',
),
);
Both rows in the fixture are added to the db table, so that's not the problem. I can access both rows via the primary keys that they're allocated. But I can't access the out of scope object in this way:
$myObjectNotInDefaultScope = $this->my_data('out_of_scope_object');
which when you're testing is really how you want to access it, I think.
I have a less than satisfactory solution in use for now of allocating the object a primary key value and using findByPk (edit: with resetScope()) to load the object. I would prefer to use the normal way of working with fixtures instead, if that's possible.
Edit: To clarify a little in response to some posts:
It is possible to use fixtures as a method to return an object. This would work:
$myObjectInDefaultScope = $this->my_data('in_scope_object');
but this wouldn't work BECAUSE it's not in the default scope and there's seemingly no way currently of running resetScope() for a fixture method call:
$myObjectNotInDefaultScope = $this->my_data('out_of_scope_object');
Why do I need to do this? Well, I might want to test my unarchive method, for example. Seems reasonable to me. (As mentioned before, I can get round this a little inelegantly by using a primary key to load the object corresponding to the fixture).
Although I can access the fixture data as an array using:
$arrayNotInDefaultScope = $this->my_data['out_of_scope_object'];
it's returning an array not an object, so I can't test the object's methods on an array.
To answer my own question, there is currently no way to use resetScope() with Yii fixtures (v 1.14). It could be implemented with some effort but given that Yii2 is on its way, it's probably not worth the effort of generating a pull request that may never make it in to the source.
For now, IMO, the cleanest workaround is:
1) Define a primary key in the fixture
2) Get the primary key from the fixture array and look up the object using it:
$arrayOutOfScopeObject = $this->my_data['out_of_scope_object'];
$myObjectNotInDefaultScope = MyObject::model()
->resetScope()
->findByPk($arrayOutOfScopeObject['id']);
You could, of course, save yourself the effort of looking up the pk from the fixture by hard-coding the pk value in your test code, but that leaves your test code vulnerable to being broken by changes to a fixture that's shared with other tests.
You are using the fixtures as a method, whilst it is an array of object.
So instead of:
$myObjectNotInDefaultScope = $this->my_data('out_of_scope_object');
You should be doing:
$myObjectNotInDefaultScope = $this->my_data['out_of_scope_object'];
Check the guide for more info
$winnerBid = Bids::model()->find($criteria);
Model has next relations:
public function relations() {
return array(
'item' => array(self::BELONGS_TO, 'Goods', 'item_id'),
'room' => array(self::BELONGS_TO, 'Rooms', 'room_id'),
'seller' => array(self::BELONGS_TO, 'RoomPlayers', 'seller_id'),
'buyer' => array(self::BELONGS_TO, 'RoomPlayers', 'buyer_id'),
);
}
When I am trying to save:
$this->seller->current_item++;
$this->seller->wins++;
$this->seller->save();
I am getting error:
Indirect modification of overloaded
property Bids::$seller has no effect
(/var/www/auction/www/protected/models/Bids.php:16)
But it was everything fine at another server?
How to fix it? Or override php directives? Any ideas? TNX
The problem here is that $seller is not a "real" property (Yii implements properties on its Models by using the magic __get method), so in effect you are trying to modify the return value of a function (which has no effect). It is as if you tried to do:
function foo() {
return 42;
}
// INVALID CODE FOR ILLUSTRATION
(foo())++;
I 'm not sure about the status of this behavior on different PHP versions, but there is an easy workaround you can use:
$seller = $this->seller;
$seller->current_item++;
$seller->wins++;
$seller->save();
I was also having the error message "Yii Indirect modification of overloaded property" when trying to massively manipulate attributes using the CActiveRecord attributes property.
Then, I discovered another method to overcome this issue, in a case where the magic method is related to an object variable which contains an array take a look: you create an AUXILIARY ARRAY in which you put the original and the new values (sometimes one wants to REPLACE a value related to one of the keys, and these methods are not satisfactory). And AFTERWARDS use an assignation, which works like the reference. For example:
$auxiliary_array = array();
foreach(Object->array_built_with_magic as $key=>$value) {
if(….) {
$auxiliary_array[$key] = Object->array_built_with_magic[$key];
} else if (…) {
$auxiliary_array[$key] = $NEW_VALUE
}
}
//So now we have the array $auxiliary_array with the
// desired MIX (that is, some originals, some modifications)
//So we will do now:
Object->array_built_with_magic =$auxiliary_array;
I had this error on yii when upgrade to php8.1,
it was in createCommand() method.
and it didn't complain in former version of php, that we access a property on model which hasn't been initialized.
the workaround was to change the bindParam() method to the bindValue().
because the former wanted to use corresponding database field which has not been initialized yet.
but the later (bindParam) just insert the value in the sql statement.