I have encountered something strange which I consider might be a bug in the framework itself so I'm wondering if there were similar experiences with it.
I have two entities: Contact and Media in a 1:M relationship. Relationship is defined as:
public function media()
{
return $this->hasMany(Media::class);
}
Now the issue I see is that when I go on show controller method and load the relation like this:
public function show(Contact $contact)
{
$contact->load('media');
return response()->json($contact);
}
The contact is resolved fine, with media relationship in the following way:
"first_name": "Melisa",
...
"media": [
{
"id": 50,
...
However if I modify the case of the relationship so that I call:
public function show(Contact $contact)
{
$contact->load('mEdIa');
return response()->json($contact);
}
It returns:
"first_name": "Melisa",
...
"m_ed_ia": [
{
"id": 50,
...
...which is really disturbing. Is it supposed to happen?
This behaviour should be noted in the documentation of Laravel, which is indeed not the case. If I were you, I would open an issue on updating the documentation about this behaviour, and see what the response will be.
But to come to terms, this is a feature, not a bug.
The nature of PHP is sensitive and insensitive in many ways. For classes and methods; PHP is insensitive.
class Contact {
public function media() {
return .. relationship;
}
}
// valid
new CONTACT();
(new contact())->mediA();
// valid reflection
$r = \ReflectionClass('contact');
$r->hasMethod('mediA'); // true
So this quirky stuff is all valid. Now to the point you said:
.. from my perspective mEdia shouldn't be allowed. My relationship
function name is media, and I want it to be used as such.
Then you have to write it as such. You have to because Laravel depends on it.
PHP is simply unable to validate/verify.
Laravel has to change the nature of a programming language where its sensitivity is deliberately designed by choice.
The expectation here is; you (as a programmer) should know about PHP's sensitivity, so in this case, there is absolutely no need to validate the given input.
The overhead to validate/verify such a thing would be tremendous and would not make sense.
$class = 'contact';
$method = 'mediA';
$r = new \ReflectionClass($class);
if($class !== $r->getName()) {
throw new \Exception('unknown class');
}
if(!in_array($method, array_map(function($method) {
return $method->name;
}, $methods))) {
throw new \Exception('unknown method');
}
// valid from here
From all points of view, the usage of $contacts->with('mediA') is completely valid.
The fact that mediA becomes medi_a using Str::snake('mediA') when converting to an array (as such), is simply a feature of Laravel you have to deal with.
There is such behaviour in Laravel. To get the same key 'mEdIa' - you have to add to media model.
public static $snakeAttributes = false;
Related
I am getting the following error and am a bit lost on it:
Livewire encountered corrupt data when trying to hydrate the …
component. Ensure that the [name, id, data] of the Livewire component
wasn’t tampered with between requests
Situation is as follows: Livewire 2.x, Laravel 7.x, Component Controller fetches data from 3 MySQL Stored Procedures and processes it. Component Blade is a pretty basic blade with foreach loop. I am using the wire:init feature so that the component is not blocking the page load. It contains a custom-built pagination. When switching to the second page of data, this error occurs. It did not error out while on Livewire 1.x.
Has anyone any idea on how to tackle this problem? The error itself does not speak much to me. Any additional info required?
Thank you in advance, appreciate any help!
In my case the solution was to make a public property protected and pass it to the blade manually, so it was excluded from Livewire's auto-handling under the hood.
To troubleshoot this open the vendor/livewire/livewire/src/ComponentChecksumManager.php file and add var_dump($stringForHashing); on line 19, just before the return statement. Then you can see the data that is being hashed and compare it to the previous hashed data to find the discrepancies.
After I did this I was able to identify the numeric keys that javascript was re-arranging and come up with an adequate fix.
One thing to note is that some json formatters will re-order numeric keys also, so it's better to compare the json without formatting it or format it manually.
Edit:
Using var_dump can interfere with the functionality on some pages, so writing the data to a file might be a better option:
file_put_contents('/path/to/log.txt', $stringForHashing . "\n\n", FILE_APPEND);
For what it worth, our issue was a very large integer, larger than what javascript can handle through Number.MAX_SAFE_INTEGER.
We filled a bug report here: https://github.com/livewire/livewire/discussions/4788 (livewire 2.10.4).
So, no solution for the bug itself when using too large integer. If you want to treat your value as a real integer you’re in no luck for now, but maybe casting to string could work for you. (and/or do your computations on the php side – with protected property – if it’s doable in your case).
That being said, the real cause of our problem was an uuid casted to int because we did not filled the protected $keyType = 'string'; of our laravel model (https://laravel.com/docs/9.x/eloquent#primary-keys)!
i run following command then it solved
php artisan optimize
So I was getting the same error, this one:
Livewire encountered corrupt data when trying to hydrate the [component] component. Ensure that the [name, id, data] of the Livewire component wasn't tampered with between requests.
After trying the accepted answer and reading this long issue thread on Livewire's Github, I played around with passing data to the view in different ways. I'm sharing it here to help someone else with the same use case issue.
I found the following change, where an url I was previous passing I just got in the view instead, made it go away. From:
// Class
class MyComponent extends Component
{
public Shop $shop;
public Address $address;
public string $action;
// rules etc
public function mount(Shop $shop)
{
if ($shop->address()->exists()) {
$this->address = $shop->address;
$this->form_action = route('merchant.shop.address.update');
} else {
$this->address = Address::make();
$this->form_action = route('address.store');
}
}
// view
<div>
<form method="post" action="{{ $action }}">
#csrf
to:
// class
class MyComponent extends Component
{
public Shop $shop;
public Address $address;
// rules etc
public function mount(Shop $shop)
{
$this->address = $shop->address()->exists() ? $shop->address : Address::make();
}
// view
<div>
<form method="post" action="{{ $shop->address()->exists() ? route('merchant.shop.address.update') : route('address.store') }}">
In my case, I had a couple of public variables and not all of them were being passed in from the parent view. After reading Markus' answer. I went and added the mount method to my livewire class and passed in the variables I was inheriting from the parent and it worked. I am using Livewire 2.3
This had the error
class MyView extends Component
{
public $fromParent, $local;
public function render()
{
return view('livewire.my-view');
}
}
Adding mount fixed it
class MyView extends Component
{
public $fromParent, $localVariable1, localVariable2;
public function mount($fromParent)
{
$this->fromParent = $fromParent;
}
public function render()
{
return view('livewire.my-view');
}
}
In my case the problem came from a database query and the resulting collection.
This query (the meaning of the variables is not important)
DB::table('progress_trackings')
->select('user_id')
->whereIn('user_id', $users->pluck('id'))
->where('object_id', $activity->id)
->where('event', $type)
->get()
->keyBy('user_id');
Produced this output:
Illuminate\Support\Collection {#2427
all: [
454 => {#2279
+"user_id": 454,
},
528 => {#441
+"user_id": 528,
},
531 => {#2368
+"user_id": 531,
},
],
The query was triggered by a button in the blade file.
Clicking the button 2 or 3 times resulted in the hydration error.
In the Livewire docs it is explained that array keys sometimes can cause problems: https://laravel-livewire.com/docs/2.x/troubleshooting
So I tried to make a plain array out of the db query result. Since I only needed the keys, this worked:
DB::table('progress_trackings')
->select('user_id')
->whereIn('user_id', $users->pluck('id'))
->where('object_id', $activity->id)
->where('event', $type)
->get()
->keyBy('user_id')
->keys()->toArray(); // ADDED THIS
Now the result is:
[
454,
528,
531,
]
Which solved the hydration error.
Addition to what others suggested above also this might happen if you have a collection which uses groupBy() might be the cause for this issue,
To fix this use protected $attribute in your component instead of public then pass $attribute to the component view.
protected $attribute;
public function mount($attribute){
$this->attribute = $attribute;
}
...........
public function render()
{
return view('livewire.view-here',['attribute'=>$attribute]);
}
I did this and it worked for me:
php artisan config:cache
php artisan config:clear
In my case it was image accessor for API i was trying to get full link image for API in model this is my mistake
public function getImageAttribute($value)
{
return $value ? url(Storage::url($value)) : '';
}
This cause me Livewire encountered corrupt data..
the solution in my case was
public function getImageAttribute($value)
{
if (Str::contains(Request()->route()->getPrefix(), 'api')) {
return $value ? url(Storage::url($value)) : '';
}
return $value;
}
In my case, I had the affected livewire component in another component.
//livewire component
<x-layouts.content-nosidebar title="false">
<div class="verification section-padding">
</div>
</x-layouts.content-nosidebar>
I removed the livewire component into its own file and I stopped getting the error.
//livewire component
<div class="verification section-padding">
</div>
Its looking, like there are some public variables that have been either set or unset (value-filled or blank by your code) using any Livewire Hook method.
Please check those and fix them, these values are not set or unset properly during the lifecycle hook method call, I also have faced this issue and fixed it.
In my case, in the updated hook method, I have changed
This
$this->{'selectGroup.'.$value} = false;
To
$this->selectGroup[$value] = false;
Worked well for me, I hope it would make sense.
I successfully fixed this by downgrading from php8 to php7.4
My project was running fine on Production, however for some reason I kept getting the "corrupt data when trying to hydrate" locally which used php8
I tried a ton of things, but eventually downgrading was the 1 thing that worked. Not sure what the difference is but try that.
I use custom class properties a lot, so in my case it was a hydration problem.
Even when any of the upstream classes is not implementing Wireable interface, you'll get the exception.
Even for enum, which was in my case.
Component
public Notifications $notifications;
public function mount()
{
$this->notifications = new Notifications([
new Notification(Type::Slack);
]);
}
Notifications class
class Notifications implements Wireable
{
public function toLivewire(): array
{
// convert to array
}
public function fromLivewire($value): self
{
// convert from array to Notifications
}
}
Notifications class
class Notification implements Wireable
{
public function toLivewire(): array
{
// convert to array
}
public function fromLivewire($value): self
{
// convert from array to Notification
}
}
Implementing enum solved my problem
I frankly didn't know that enums can implement interfaces.
enum Type: int implements Wireable
{
case Slack = 1;
case Teams = 2;
public function toLivewire(): int
{
return $this->value;
}
public static function fromLivewire($value): Type
{
return self::from($value);
}
}
Hope that helps.
In my case, the Livewire component was referencing a model that had a custom attribute which was calculated using Carbon::now()
So that attribute had a different value every time the component tried to hyrdrate, and therefore was "corrupted".
I faced this error, when I added an array of a Value Object into the livewire component.
It was a very simple value object class like this:
class Foo {
public string $name;
public string $data;
}
After several hours of struggling, it became clear that solution was simply adding a $id property to the Foo class:
class Foo {
public string $id; // a unique id
public string $name;
public string $data;
}
By adding $id, livewire now can keep state of a collection of objects.
I solved it by putting livewire scripts #livewireScripts inside page header
<head>
<meta charset="utf-8" />
#livewireStyles
#livewireScripts
</head>
I am using the Fractal library to transform a Book object into JSON by using a simple transformer:
class BookTransformer extends \League\Fractal\TransformerAbstract
{
public function transform(Book $book)
{
return [
'name' => $book->getName()
// ...
];
}
}
And I am performing the transformation as follows.
$book = new Book('My Awesome Book');
$resource = new \League\Fractal\Resource\Item($book, new BookTransformer());
$fractal = new \League\Fractal\Manager();
$fractal->setSerializer(new \League\Fractal\Serializer\ArraySerializer());
$json = $fractal->createData($resource)->toJson();
This works great. However, I have certain fields on my Book object that should not always be included, because this depends on the context the transformation is done in. In my particular use case, the JSON returned to AJAX requests from my public website should not include sensitive information, while this should be the case when the data is requested from an admin backend.
So, let's say that a book has a topSecretValue field, which is a string. This field should not be included in one transformation, but should be included in another. I took a look at transformer includes, and played around with it, but this only works with resources. In my case, I need to somehow include different fields (not resources) for different contexts. I have been digging around and could not find anything in the Fractal library that could help me, but maybe I am missing something?
I came up with a working solution, but it is not the prettiest the world has ever seen. By having a BaseBookTransformer that transforms fields that should always be included, I can extend this transformer to add fields for other contexts, e.g. AdminBookTransformer or TopSecretValueBookTransformer, something like the below.
class AdminBookTransformer extends BookTransformer
{
public function transform(Book $book)
{
$arr = parent::transform($book);
$arr['author'] = $book->getTopSecretValue();
return $arr;
}
}
This works fine, although it is not as "clean" as using includes (if it were possible), because I have to actually use a different transformer.
So the question is: is there anything in Fractal that enables me to accomplish this in a simpler/cleaner way, or is there a better way to do it, be it the Fractal way or not?
I am sorry for asking this type of question but I'm getting really frustrated by this situation
This is my controller
public function changeName_PUT() {
header('Content-Type: application/json');
$return = \Models\User::getCurrent()->changeName(Input::data());
return json_encode($return);
}
And this is my model
public function changeName($data){
$rules = array( ... );
$messages = array( ... );
$valid = Input::validate($data, $rules, $messages);
if($valid === true){
return $this->putIntoDb(array('name' => $data['name'])) ? 'Namechanged successfully.' : 'An error occurred.';
}else{
return $valid->getMessage();
}
}
My problem is that no matter whether the model succeeds or fails there is always a string returned, so in the controller I cannot determine if the name was changed or not. If I return only true or false then in the controller, if something failed, I wouldn't know what it was. My other option is to return arrays from the model like array('error', 'message') but that looks so ugly and gives me the feeling that I'm doing it wrong. Can you point me the right way?
You have different approaches :
Simple version : If you insert, return the inserted_id|false. If you delete, return true|false. If you update, return true|false. If you select, return array|string|false.
Class version : You return true or false (except for select which returns the queried string|array). And you store in a class variable the error messages so that you can do something like :
$success = $model->query('blabla');
if (!$success)
print_r($model->getMessages());
Object version : Same as the simple version, except that you return the database object on a successful select or update query (else false).
Exception version : Same as object version, but instead of returning false, you throw an error that describes the problem.
Now, if you look at common frameworks you will see that CodeIgniter uses the object version, Phalcon uses the class version, Zend uses the exception version.
There is no "best-way", just use a method and stick with it for all your models.
(Just do not return a string that explains the error on a failed query!)
Model layer should not be sending data to the controllers at all.
In the MVC architectural pattern the controllers are responsible for altering the state of model layer. And that's it. View instances then pull what information they need from said model layer.
If, because of controller's alterations, an error state is triggered in the model layer, then controller does not care about it. It is only important to the view, which, based on whether an error happened, might pick a different set of templates for assembly of response.
P.S.: the HTTP headers are part of response. So, why are your "controllers" generating a response? It's basically no different from putting an echo there.
Recently, I wrote a simple web application.
I currently have.
A static page which serves the html markup and the resources I need
A javascript routing handling mechanism which communicate with the server and render responses on the client.
For each object I need to manipulate, the server provides a php script at /app/object.php which accepts POST data and return JSON results.
Eg (not actual responses):
POST /app/comments.php?a=add&page_id=43&author=A&text=Some%20Text
{"s":"OK"}
POST /app/comments.php?a=list&page_id=43
[{ "author": 'A', "text": "Some text"}, { "author": "B", "text": "Other text"}]
POST /app/users.php?a=list
["A", "B", "C"]
Under the hood the JSON api is implemented like that:
//comments.php
require('init.php');
// start sessions, open database connections with data in config.php
switch(POST('a')){
case "list":
//.... retrieving data
echo json_encode($data)
break;
case "add":
//.... inserting data
echo '{"s":"OK"}';
break;
}
The largest object has 7 methods and 200 (well-indented, not compressed) LOC, while the average is roughly 3 methods per object.
A developer friend of mine is suggesting to replace the switch with objects, to make it "simpler", "more extensible" and "more maintainable".
I frankly don't see how a system like that could get any simpler (especially by using objects) but I'd love to know other developers' opinions.
Ignoring the performance hit of using objects in PHP, should I use a class-based approach?
If yes, how can I structure my JSON API in order to use objects, without adding too much code (and thus decreasing the maintainability of the project)?
<?php
class Controller {
protected $post;
public function __construct() {
$this->post = $post;
}
public function __call($name, $arguments) {
if(!method_exists($this, $name)) {
die("Unknown action!");
}
}
public function whatever() {
echo json_encode($this->post['page_id']);
}
public function data() {
echo '{"s":"OK"}';
}
// new action? just add another method
}
$controller = new Controller();
$controller->{$_POST('a')}(); // example 1
$controller->data(); // you can't do it using switch
Easy to add new methods
Easy to maintance
You can fire your methods whenever your want
Code is tidy
It's really common practice
I'm working on a Symfony project (my first) where I have to retrieve, from my Widget class, a set of widgets that belong to a Page. Before returning the results, though, I need to verify--against an external service--that the user is authorized to view each widget. If not, of course, I need to remove the widget from the result set.
Using CakePHP or Rails, I'd use callbacks, but I haven't found anything similar for Symfony. I see events, but those seem more relevant to controllers/actions if I'm reading things correctly (which is always up for discussion). My fallback solution is to override the various retrieval methods in the WidgetPeer class, divert them through a custom method that does the authorization and modifies the result set appropriately. That feels like massive overkill, though, since I'd have to override every selection method to ensure that authorization was done without future developers having to think about it.
It looks like behaviors could be useful for this (especially since it's conceivable that I might need to authorize other class instances in the future), but I can't find any decent documentation on them to make a qualified evaluation.
Am I missing something? It seems like there must be a better way, but I haven't found it.
First of all, I think behavior-based approach is wrong, since it increases model layer coupling level.
There's sfEventDispatcher::filter() method which allows you to, respectively, filter parameters passed to it.
So, draft code will look like:
<somewhere>/actions.class.php
public function executeBlabla(sfWebRequest $request)
{
//...skip...
$widgets = WidgetPeer::getWidgetsYouNeedTo();
$widgets = $this->getEventDispatcher()->filter(new sfEvent($this, 'widgets.filter'), $widgets));
//...skip...
}
apps/<appname>/config/<appname>Configuration.class.php
//...skip...
public function configure()
{
$this->registerHandlers();
}
public function registerHandlers()
{
$this->getEventDispatcher()->connect('widgets.filter', array('WidgetFilter', 'filter'));
}
//...skip
lib/utility/WidgetFilter.class.php
class WidgetFilter
{
public static function filter(sfEvent $evt, $value)
{
$result = array();
foreach ($value as $v)
{
if (!Something::isWrong($v))
{
$result[] = $v;
}
}
}
}
Hope you got an idea.
Here's some documentation on Symfony 1.2 Propel behaviors: http://www.symfony-project.org/cookbook/1_2/en/behaviors.
Why not just have a 'getAllowedWidgets' method on your Page object that does the checks you're looking for? Something like:
public function getAllowedWidgets($criteria = null, PropelPDO $con = null) {
$widgets = $this->getWidgets($criteria, $con);
$allowed = array();
// get access rights from remote service
foreach($widgets as $widget) {
// widget allowed?
$allowed[] = $widget;
}
return $allowed;
}
However, if you always want this check to be performed when selecting a Page's Widgets then Propel's behaviours are your best bet.
Although, at least in theory, I still think that a behavior is the right approach, I can't find a sufficient level of documentation about their implementation in Symfony 1.4.x to give me a warm and fuzzy that it can be accomplished without a lot of heartburn, if at all. Even looking at Propel's own documentation for behaviors, I see no pre- or post-retrieval hook on which to trigger the action I need to take.
As a result, I took my fallback path. After some source code sifting, though, I realized that it wasn't quite as laborious as I'd first thought. Every retrieval method goes through the BasePeer model's doSelect() method, so I just overrode that one in the customizable Peer model:
static public function doSelect( Criteria $criteria, PropelPDO $con = null ) {
$all_widgets = parent::doSelect( $criteria, $con );
$widgets = array();
foreach ( $widgets as $i => $widget ) {
#if( authorized ) {
# array_push( $widgets, $widget );
#}
}
return $widgets;
}
I haven't wired up the service call for authorization yet, but this appears to work as expected for modifying result sets. When and if I have to provide authorization for additional model instances, I'll have to revisit behaviors to remain DRY, but it looks like this will suffice nicely until then.