I have this weird behaviour from Typo3 6.2 LTS.
In my extension I have a Model with a FileReference Property. This property has a vaule != 0. This value does exist in sys_file_reference table.
Not the weird magic happens. If I try to access this file, I do only get a nullvalue instead of a FileReference- / FileObject.
We already cleared our cache (server and browser) but nothing. It's still null.
I appreciate every kind of help!
Greetz, Paddaels
I remember it was always hard to make a 1:1 relation from a domain model to a FileReference. I suggest you to use existing patterns and work with a ObjectStorage for that purpose.
You can copy the neccessary TCA from the existing tca of the tt_content table (field image for example). The Property annotation should look like:
/**
* #var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Vendor\Extension\Domain\Model\FileReference>
* #lazy
* #cascade remove
*/
protected $propName;
Of course you have to create the FileReference Model in your own namespace. But you can extend the Extbase basemodel, so you dont have to write any methods.
To map your model to the sys_file_reference table you have to add some typoscript.
For that purpose create a ext_typoscript_setup.txt in your extensions folder and insert the following code (adjust namespace and modelname)
config.tx_extbase.persistence.classes {
Vendor\Extension\Domain\Model\FileReference.mapping {
tableName = sys_file_reference
}
}
After clearing the caches in the install tool (and making database migrations of course) it should work.
Explanations:
#lazy: Typo3 wont fetch all references at once, only if the property is accessed.
#cascade remove: Extbase will delete all sys_file_reference records related to your domain model once the model is deleted.
Related
Using Laravel 8 with cviebrock sluggable, my models do not update when I made a change.
When I remove the use of sluggable from my models, they work! Showing that sluggable is the cause.
It turns out that there is a breaking change, which occurred on Laravel 8 for me.
Solution
The solution is to add the following code to each model for which you use sluggable on:
public function sluggableEvent(): string
{
return SluggableObserver::SAVED;
}
One can find a reference to the breaking change on the cviebrock/eloquent-sluggable github page: https://github.com/cviebrock/eloquent-sluggable
Slug no default value error, as a consequence of this change
You may get an error that your slug field has no default value, when SAVING, that is because the model will be updated without the slug, which will be updated when the model is SAVED.
To solve this problem, you can set the slug field to nullable() in the migration file, or at the database level make the field nullable and set the default value to NULL.
Observer event changes
Previously, one could check for slug changes using EventObserver#updating. This no longer works.
Instead, one can register a model event for 'slugging' or 'slugged' in AppServiceProvider.php.
For example (I have a model for events called Event, whereby I change the slug if an event date or title is changed):
Event::registerModelEvent('slugged', static function($event) {
// $event->slug (the new slug)
// $event->getOriginal('slug') (the old slug)
}
Bare in mind, for a newly created DB row for a model, $event->getOriginal('slug') will be NULL.
The "slugging" event is fired just before the slug is generated.
The "slugged" event is fired just after a slug is generated.
I need to make product urls like this "attribute1/attribute2/product-url-key" dynamically. I've found that the urls of every product are in the enterprise_url_rewrite table, request path field.
I would like to make change to the indexer process as well, so the changes will stay when its rerun, but I don't know where it is?
Good afternoon! Let's break this down:
I need to make product urls like this "attribute1/attribute2/product-url-key" dynamically
Yes - you can create URL rewrites dynamically using the Magento models that represent the database table you've identified already:
/** #var Enterprise_UrlRewrite_Model_Redirect $rewrite */
$rewrite = Mage::getSingleton('enterprise_urlrewrite/redirect');
// Create new record or load the existing one
$rewrite->loadByRequestPath($requestUrl, $store->getId());
$rewrite
->setStoreId($store->getId()) // define which store the rewrite should be
->setOptions(null) // specify any rewrite/redirect/custom options
->setRequestPath($requestUrl) // specify the request URL
->setIdentifier($requestUrl)
->setTargetPath($targetPath) // specify the redirect target
->setEntityType(Mage_Core_Model_Url_Rewrite::TYPE_CUSTOM)
->setDescription('Add a comment if you want to');
$rewrite->save();
This will attempt to load an existing URL rewrite/redirect by the $requestUrl and will return an empty model if it was not found which you can embellish with your data and save.
The "options" define whether it's a temporary or permanent redirect (302 vs 301).
More information here via the Magento EE user guide.
I would like to make change to the indexer process as well, so the changes will stay when its rerun, but I don't know where it is?
Don't worry about it. The (modern) Magento database has table triggers wherever records need to be indexed, and will detect creations, updated and deletions on those tables. The indexers will detect that change need to be made and will make them for you as required.
If you're seeing URL rewrites disappearing it's most likely because you've been adding them directly to the index table with SQL, so the table is rewritten whenever the indexer runs. To avoid this use the model as noted above and everything will be saved into the correct location and indexed properly.
I didn't understand what is the effect of the default option in the migrations.
I can see that the column in the database is defined with default value, but the models are ignore it completely. Say I have a Book model that reflect the books table in the database. I have migration to create the books table:
Schema::create('books', function (Blueprint $table) {
$table->increments('id');
->string('author');
->string('title');
->decimal('price', 4, 1)->default(100);
->timestamps();
});
When I create a new instance of Book model I see:
$book = new Book();
var_dump($book->price); //Always 0...
The default value is ignored and the attribute is not sets correctly.
Ok, I can get it, because it is a new object and it shouldn't get the default values from the DB. But if I tries to save model like:
$book = new Book();
$book->author = 'Test'
$book->title = 'Test'
$book->save();
It is saves 0 in the field price in the database!
So what is the point of the default option in the migrations?
By the way...
It wasn't be better if the model see inside the migration (if exists) what are the fields types and behavior instead to define it manually in the model and the migration? And moreover, even to create a validator automatically for the model. I think that it was possible with small change of the migration structure, so why it is not like that?
Put the default value in single quote and it will work as intended. An example of migration:
$table->increments('id');
$table->string('name');
$table->string('url');
$table->string('country');
$table->tinyInteger('status')->default('1');
$table->timestamps();
EDIT : in your case ->default('100.0');
In Laravel 6 you have to add 'change' to your migrations file as follows:
$table->enum('is_approved', array('0','1'))->default('0')->change();
You can simple put the default value using default(). See the example
$table->enum('is_approved', array('0','1'))->default('0');
I have used enum here and the default value is 0.
Might be a little too late to the party, but hope this helps someone with similar issue.
The reason why your default value doesnt't work is because the migration file sets up the default value in your database (MySQL or PostgreSQL or whatever), and not in your Laravel application.
Let me illustrate with an example.
This line means Laravel is generating a new Book instance, as specified in your model. The new Book object will have properties according to the table associated with the model. Up until this point, nothing is written on the database.
$book = new Book();
Now the following lines are setting up the values of each property of the Book object. Same still, nothing is written on the database yet.
$book->author = 'Test'
$book->title = 'Test'
This line is the one writing to the database. After passing on the object to the database, then the empty fields will be filled by the database (may be default value, may be null, or whatever you specify on your migration file).
$book->save();
And thus, the default value will not pop up before you save it to the database.
But, that is not enough. If you try to access $book->price, it will still be null (or 0, i'm not sure). Saving it is only adding the defaults to the record in the database, and it won't affect the Object you are carrying around.
So, to get the instance with filled-in default values, you have to re-fetch the instance. You may use the
Book::find($book->id);
Or, a more sophisticated way by refreshing the instance
$book->refresh();
And then, the next time you try to access the object, it will be filled with the default values.
The problem you face is not about the migration file. The value turns into 0 every time because you didn't change $fillable on your model. When you forget to add recently added column to $fillable of its model, Laravel inserts 0.
I am working in a project that has a large postgreSQL database. The previous project was developed in Java from scratch. We are now developing that in Laravel. The previous system had user management system similar to Zizaco/entrust. So, we used in our system as well. The previous table had module table instead of permission table used in entrust. We have already configured that by changing the table name in config/entrust.php. However, the previous system has permission_name instead of name field used in entrust. How do I config entrust to use the unique permission_name instead of name field.
I am looking for a solution, so that we don't have to change in the sources of entrust because then upgrading it would break the system. Can it be configured in the model?
The Entrust package is hardcoded to use the name attribute, so there is no configuration value or anything to change that. However, one thing you can attempt is to define an accessor and mutator for the name attribute.
In your App\Permission model, define the following functions:
class Permission extends Model {
// accessor
public function getNameAttribute($value) {
return $this->permission_name;
}
// mutator
public function setNameAttribute($value) {
$this->attributes['permission_name'] = $value;
}
}
Documentation for accessors and mutators: http://laravel.com/docs/5.0/eloquent#accessors-and-mutators
Before I describe my problem, it might actually make it clearer if I start with the error I'm getting:
$ ./app/console doc:mig:diff
[Doctrine\DBAL\Schema\SchemaException]
The table with name 'user_media_area' already exists.
That's absolutely true - user_media_area does exist. I created it in a previous migration and I don't understand why Symfony is trying to create the table again.
My problem has something to do with a many-to-many relationship. I have a table called user, a table called media_area and a table called user_media_area.
Here's the code where I tell user about media_area (Entity/User.php):
/**
* #ORM\ManyToMany(targetEntity="MediaArea", inversedBy="mediaAreas")
* #JoinTable(name="user_media_area",
* joinColumns={#JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#JoinColumn(name="media_area_id", referencedColumnName="id")}
* )
*/
private $mediaAreas;
And here's where I tell media_area about user (Entity/MediaArea.php):
/**
* #ORM\ManyToMany(targetEntity="User", mappedBy="users")
*/
private $users;
What's interesting is that if I remove that JoinTable stuff from Entity/User.php, ./app/console doctrine:migrations:diff will work again:
/**
* #ORM\ManyToMany(targetEntity="MediaArea", inversedBy="mediaAreas")
*/
private $mediaAreas;
However, it's a little off: it now wants to create a new table called mediaarea, which I don't want. My table already exists and it's called media_area.
So it looks like either way, Symfony is trying to create a table based on this ManyToMany thing in my User class, and the only reason the problem goes away when I remove the JoinTable is that the name of the table it wants to create (mediaarea) no longer matches the actual name of my table (media_area).
So my question is: Why does it want to create a new table at all? What am I doing wrong?
(I know it's possible that my naming conventions are off. Symfony and Doctrine's database examples are frustratingly devoid of multi-term column names, so I don't always know if I'm supposed to do media_area or mediaArea.)
According to the Association Mapping explanation on the official docs, the #JoinColumn and #JoinTable definitions are usually optional and have sensible default values, being:
name: "<fieldname>_id"
referencedColumnName: "id"
From that we can conclude that there is really no concrete difference between the two implementations you presented.
However, when it comes to migration, the creation of the table is a pretty common and expected behaviour. The thing is the table should always get deleted and created again, which is not happenning.
About the table name issue, the default behaviour of Doctrine 2 about this:
/**
* #ORM\ManyToMany(targetEntity="MediaArea", inversedBy="mediaAreas")
*/
private $mediaAreas;
Is to try and create a table called mediaarea. Again, perfectly normal.
If you want to declare a specific name for the table of an entity, you should do this:
/**
* #ORM\Table(name="my_table")
*/
class Something
I'm not sure if that helps you at all, but I guess it puts you, at least, on the right track.