I am struggling to change the timestamp column names which are generated by
php artisan migrate
command.
I have already made following change. When I use eloquent query builder, it can correctly generate the column name, but when I using the command above, it still generates "created_at", "updated_at" and "deleted_at". Can anyone help me out? Thanks a lot.
/* vendor\framework\src\Illuminate\Database\Eloquent\Model.php */
/**
* The name of the "created at" column.
*
* #var string
*/
const CREATED_AT = 'datetime_created';
/**
* The name of the "updated at" column.
*
* #var string
*/
const UPDATED_AT = 'datetime_updated';
/**
* The name of the "deleted at" column.
*
* #var string
*/
const DELETED_AT = 'datetime_deleted';
/* vendor\framework\src\Illuminate\Database\Schema\Blueprint.php */
/**
* Indicate that the timestamp columns should be dropped.
*
* #return void
*/
public function dropTimestamps()
{
$this->dropColumn('datetime_created', 'datetime_updated');
}
/**
* Add a "deleted at" timestamp for the table.
*
* #return void
*/
public function softDeletes()
{
$this->timestamp('datetime_deleted')->nullable();
}
/**
* Add creation and update timestamps to the table.
*
* #return void
*/
public function timestamps()
{
$this->timestamp('datetime_created');
$this->timestamp('datetime_updated');
}
/**
* Add a "deleted at" timestamp for the table.
*
* #return void
*/
public function softDeletes()
{
$this->timestamp('datetime_deleted')->nullable();
}
P.S. I know it's not a good idea to modify the "core". If someone can tell me the best way to extend those classes I would really appreciate it.
Don't ever edit the code under the vendor folder. First, it's usually (by default) not carried with your repository, so you'd lose the changes if you or anyone else ever wanted to work on another machine. Second, it'd be overwritten at the moment you do a composer update.
Well, that being said, let's start dealing with this "modifying the core" horror. For the Illuminate\Database\Eloquent\Model.php, simply create a base model, from which you'll be extending all your subsequent models, and overwrite the constants in it:
app/models/BaseModel.php
abstract class BaseModel extends Eloquent {
/**
* The name of the "created at" column.
*
* #var string
*/
const CREATED_AT = 'datetime_created';
/**
* The name of the "updated at" column.
*
* #var string
*/
const UPDATED_AT = 'datetime_updated';
/**
* The name of the "deleted at" column.
*
* #var string
*/
const DELETED_AT = 'datetime_deleted';
}
Then, for the Illuminate\Database\Schema\Blueprint case... Well, it gets bloody:
Extend ..\Schema\Blueprint, overwriting the methods you mentioned.
Extend ..\Schema\Builder, overwriting createBlueprint method to use your new Blueprint class.
Also extend ..\Schema\MySqlBuilder to extend from your new Builder class.
Extend ..\Connection, overwriting getSchemaBuilder method to use your new Builder class.
Also extend ..\MySqlConnection, ..\PostgresConnection, ..\SqlServerConnection and ..\SQLiteConnection to extend from your new Connection class.
Note: ..\MySqlConnection also needs to have its getSchemaBuilder method extended to use your new MySqlBuilder class.
Extend ..\ConnectionFactory, overwriting createConnection method to use your extended Connection classes.
Create a ServiceProvider to register your new ConnectionFactory class as the new db.factory component, and add it on your app/config/app.php file, under providers.
So, after half an hour digging through Laravel's source code to figure that out, I came to the conclusion that it would probably be easier to simply do the following on your migrations:
$table->timestamp(BaseModel::CREATED_AT);
$table->timestamp(BaseModel::UPDATED_AT);
Related
So I have a following single inheritance table defined:
/**
* #Entity
* #Table(name="listKeys")
* #InheritanceType("SINGLE_TABLE");
* #DiscriminatorColumn(name="parent", type="string")
* #DiscriminatorMap({"Type1" = "Something1", "Type2" = "Something2"})
*/
abstract class ListKey
{
/**
* #OneToMany(targetEntity="KeyListValue", mappedBy="listKey", cascade={"persist", "remove"}, orphanRemoval=true)
*/
private Collection $listValues;
/**
* #ManyToOne(targetEntity="KeyListValue")
* #JoinColumn(name="defaultKeyListValueId")
*/
private ?KeyListValue $defaultKeyListValue = null;
}
With the list options defined as:
/**
* #Entity
* #Table(name="keyListValues")
*/
class KeyListValue
{
/**
* #Column(type="string")
*/
private $label;
/**
* #ManyToOne(targetEntity="ListKey", inversedBy="listValues", cascade={"persist"})
* #JoinColumn(name="caKeyId")
*/
private ListKey $listKey;
}
Before adding list values to a key, persisting and flushing worked ok, so the key would get inserted first after the commit order calculation. But once I added a new column default key list value, the commit order changes, and key list values try to get inserted first so the transaction fails.
Workaround is that I use flush() after creating new key and the adding the list values but this is problematic since ListKey also has association with some other entities which are not persisted yet at the time of flush so cascades would have to be declared and I don't want that. Any suggestion on how I can redefine this relationship better or a better workaround so the commit order would first insert keys?
class DistCache
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\PlaceInfo")
* #ORM\JoinColumn(nullable=false)
*/
private $placeOne;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\PlaceInfo")
* #ORM\JoinColumn(nullable=false)
*/
private $placeTwo;
This table has two members, both related to PlaceInfo class.
And PlaceInfo class doesn't have any member related to DistCache class.
Then I want to delete the DistCache entry when one of two members(placeOne or placeTwo) is deleted
I googled around and found cascade="remove" or orphanRemoval=true, but both looks bit different my purpose.
How can I solve?
I can see that for both PlaceInfo object you set nullable=false , so when deleting a PlaceInfo, not only have to delete the DistCache entities managed by entityManager, you have to delete the ones in the database too.
I suggest you can use preRemove event from Doctrine life cycle callbacks.
On the remove event of a PlaceInfo record, you query all the DistCache objects which use the deleted PlaceInfo object and remove them first.
In short, you need to :
Add #ORM\HasLifecycleCallbacks before your class to enable life cycles.
Add preRemove function in PlaceInfo class :
/**
* #ORM\PreRemove
* #param LifecycleEventArgs $event
*/
public function removeDistCache(LifecycleEventArgs $event)
{
$em = $event->getEntityManager();
// Use $em to query and delete the DistCache entities
}
I am using symfony 2, I have one field in database "old_status". I want to change it to "status" field (change in both database + entity. I changed it because It is making the developer confuse)
/**
* #var integer
*
* #ORM\Column(name="status", type="smallint", nullable=false, options={"unsigned"=true})
*
* #Expose
*/
private $status;
So in the response it will change to "status". But I don't want to change the contract. I want to show both of "old_status" and "status" fields but with same value. So the current partner will continue use "old_status" until they move to new field. The new partner will use "status".
I don't want add more field in database, I want to handler it by use entity.
Can I do it with entity?
You can simply leave the old getter and make them both new and old methods use the new field name status.
something like:
public function getStatus() {
return $this->status;
}
/**
* #deprecated Renamed to getStatus
*/
public function getOldStatus() {
return $this->status;
}
You can do a similar thing with other methods, like setters, if needed.
Since $status is a private field, it's just a matter of public interface.
I have a many-to-many-relation, and when I load an entity that is on one side this relation, I expect to see as its property the ArrayCollection of related entities on another side. However, this does not happen - the ArrayCollection loaded has no elements in it, while in the database I can see the related entries. What could be the reason?
Here is my code:
One side of the relation, ConsolidatedReport class:
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="P24\Response", inversedBy="consolidatedReports")
* #ORM\JoinTable(name="con_rprt_responses")
*/
private $responses;
Another side of the relation, Response class:
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="P24\ConsolidatedReport\ConsolidatedReport", mappedBy="responses")
*/
private $consolidatedReports;
Here is the function I run to get an instance of ConsolidatedReport. This function sits inside a service that is being called from container:
/**
* Picks the consolidated report with given id.
*
* #param string $id
*
* #return ConsolidatedReport
*
* #throws NonExistentConsolidatedReportException if the survey doesn't exist
*/
public function pick($id)
{
$report = $this->repository->findOneBy(array('id' => $id));
if (!$report) {
throw new NonExistentConsolidatedReportException($id);
}
return $report;
}'
In the database, there is "con_rprt_responses" table with two columns "consolidated_reports_id" and "response_id". However, in profiler I do not see any queries to that table.
What could go wrong here?
UPDATE:
Please see my answer to this question below, that worked for me.
I added fetch="EAGER" to the $responses property of ConsolidatedReport class, and it worked.
The code now looks like this:
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="P24\Response", inversedBy="consolidatedReports", fetch="EAGER")
* #ORM\JoinTable(name="con_rprt_responses")
*/
private $responses;
More info here:
http://doctrine-orm.readthedocs.org/en/latest/reference/working-with-objects.html#by-eager-loading
Still if someone knows why the collection of related entity would not load without explicitly specifying EAGER fetching - please share your knowledge, it is highly appreciated!
If you specify the joinColumns, does this solve your problem?
/**
* #ORM\ManyToMany(targetEntity="P24\Response", inversedBy="consolidatedReports")
* #ORM\JoinTable(name="con_rprt_responses",
* joinColumns={#ORM\JoinColumn(name="consolidated_reports_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="response_id", referencedColumnName="id")}
* )
*/
The *toMany properties have to be initialized with an ArrayCollection.
public function __construct() {
$this->responses = new \Doctrine\Common\Collections\ArrayCollection();
$this-> consolidatedReports = new \Doctrine\Common\Collections\ArrayCollection();
}
In case you have more then single query to fetch the same objects using Doctrine try to use:
$entityManager->clear();
in between, to fix "missing" entities. It isn't solution "as is", however can give you an idea something wrong in chain of your queries.
I'm using php-activerecord for a short while now and i absolutely love it. Php-activerecord is an open source ORM library based on the ActiveRecord pattern. However, i also like phpdoc and use that to document my code. Therefore it's easy for my coworkers to understand the platform's i build. But with php-activerecord in use my Model classes now look like this:
/**
* The Company class.
*
* #since 1.0.0
*
*/
class Company extends \ActiveRecord\Model
{
/** explicit table name since our table is not "company" */
static $table_name = 'Company';
/** explicit pk since our pk is not "id" */
static $primary_key = 'companyId';
}
They work, but they used to look like this:
/**
* The Company class.
*
* #since 1.0.0
*
*/
class Company extends \ActiveRecord\Model
{
/** #var integer The company id. */
private $companyId;
/** #var string The company name. */
private $name;
}
Long story short
With php-activerecord in use there's no way to document my model attributes and update phpdoc. I want to be able to do this, in what direction should i look?
You can document all your "magic" properties as an #property! Say you have a table "Company" with fields "id, name, location", you would end up with:
**
* #property int $id
* #property string $name
* #property string $location
*/
class Company extends \ActiveRecord\Model
{
}
You can see in the documentation that there are some other tricks, like "property-read". I use those for any connections you might have, as you can read a has-one for instance, but cannot write models to those connections.
So if a Company has employees that you have defined as a $employees, you might add
* #property-read Employee[] $employees
and so on.