BadMethodCallException Call to undefined method Illuminate\Database\Eloquent\Relations\BelongsToMany::citas() - eloquent

I need to obtain the appointments of a patient who is in the clinics that have a doctor.
A doctor may have several clinics associated with him.
I have a 1:n relationship between appointments and clinics. 1 appointment occurs at 1 clinic and 1 clinic has many appointments.
My Models:
class Cita extends Model
{
public function clinica()
{
return $this->belongsTo('\App\Models\Clinica');
}
}
class Clinica extends Model
{
public function citas()
{
return $this->hasMany('App\Models\Cita')->withTimestamps();
}
}
And in my controller I have:
public function indexpaciente($paciente_id)
{
$paciente = Paciente::find($paciente_id);
$clinicas = User::find(Auth::user()->id)->clinicas()->where('estado', '=', 1); // clinicas del doctor
$citas = $clinicas->citas()->where('paciente_id', '=', $paciente_id)->get(); // citas asociadas a las clinicas del doctor
dd($citas);
The citas table is as follows:
public function up()
{
Schema::create('citas', function (Blueprint $table) {
$table->id();
$table->string('title')->nullable;
$table->string('descripcion')->nullable;
$table->dateTime("start")->nullable();
$table->dateTime("end")->nullable();
$table->unsignedBiginteger('paciente_id')->nullable(); // paciente
$table->foreign('paciente_id')->references('id')->on('pacientes')->onDelete('cascade');
$table->unsignedBiginteger('profesional_id')->nullable(); // doctor que lo atendió
$table->foreign('profesional_id')->references('id')->on('users')->onDelete('cascade');
$table->unsignedBiginteger('clinica_id')->nullable(); // clinica
$table->foreign('clinica_id')->references('id')->on('clinicas')->onDelete('cascade');
$table->unsignedBiginteger('creadapor_id')->nullable(); // quien la creó
$table->foreign('creadapor_id')->references('id')->on('users')->onDelete('cascade');
$table->integer('roldelquelacreo')->default(1); // rol a quien pertenece para calculo de comisiones
$table->string('encabezado')->nullable(); // encabezado de la epicrisis
$table->string('glosa','500')->nullable(); // solo para los proefesionales
$table->unsignedDecimal('total', $precision = 11, $scale = 2)->nullable(); //suma de las prestaciones con 2 decimales max: $99.999.999,99
$table->integer('estado')->default(1); // 1= cita creada 0= cita bloqueada
$table->timestamps();
});
}
I have both models defined as appropriate in a 1:n relationship but it still tells me that appointments are undefined.

where does not return a result, it is a builder method. You are using what is returned from where as a result (Model in this case) when it is still the builder that is building the query. You would have to execute the query to get a result. Even if you get the result, it seems like you want to get many results, you would get a Collection back from get, which wouldn't have a relationship method citas. So there are multiple problems here.
This would get you many "clinicas":
$clinicas = Auth::user()->clinicas()->where('estado', '=', 1)->get();
Which would be a Collection of Models, so not sure what particular Model you would want to be calling the relationship method on.
This would get you 1 'clinica':
$clinica = Auth::user()->clinicas()->where('estado', '=', 1)->firstOrFail();
Then you could call the relationship method on this result:
$clinica->citas()->....;
Your error message does not match the code you have provided as you have not defined a Belongs To Many relationship here.

Related

Lumen Eloquent Relations

UPDATE
My approach is to receive something equal to a simpel mysql join. I want all entries from tabel tx with project_id = x joined with txevent on tx.id = txevent.tx_id.
I´m using lumen 5.5
UPDATE 2
The following raw sql will do exactly what I want:
$rawTx = DB::>table('tx')
->join('txevent', 'txevent.tx_id', '=', 'tx.id')
->where('tx.project_id', $request->get('project_id'))
->where('txevent.short_id', $request->get('short_id'))
->get();
Isn´t it possible to achieve the same with relations?
END UPDATE
I´ve got 2 tables tx and txevent:
tx:
id, int, autoincrement
project_id, int
txevent:
id, int, autoincrement
tx_id, int, fk(tx.id)
shortnumber, char
in tx.php I´ve got the following method:
public function txeventWithShortnumber($shortnumber)
{
return $this->hasOne('App\Txevent')->where('shortnumber', $shortnumber)->first();
}
in TxController.php I´ll do:
$tx = Tx::where('project_id', $request->get('project_id'))->txeventWithShortnumber($request->get('shortnumber'))->get();
as result I get the following error message:
(1/1) BadMethodCallException
Call to undefined method Illuminate\Database\Query\Builder::txeventWithShortnumber()
in Builder.php line 2483
Can someone tell me what I´m doing wrong?
I recommend to do it this way:
In Tx.php
public function txEvents()
{
return $this->hasMany(TxEvent::class);
}
public function txeventWithShortnumber($shortNumber)
{
return $this->txEvents->first(function($txevent) use ($shortNumber) {
return $txevent->shortnumber == $shortNumber;
});
}
In controller:
$tx = Tx::where('project_id', $request->get('project_id'))->get();
// attach shortNumber event
$tx->map(function($t) use ($request) {
$t->eventWithShortNumber = $t->txeventWithShortnumber($request->get('shortnumber'));
});
$tx variable will now hold txEvents with the given short number as well.
I am not sure if you can dynamically pass condition to an eloquent relationship.

Get set not covering in Test Class - Apex

Getter , setter in test class not getting covered
Here is the code ?
Method :
public static List<SelectOption> UserList
{
get
{
/*string role='';
if(issueTeam == 'Contracts')
role = 'Contract Owner';
else if(issueTeam == 'Buyer')
role = 'Buyer';
else
role = 'Master Data Allocator';*/
UserTemp = [Select u.LastName, u.Id, u.FirstName,u.Name, u.Email From User u ORDER BY u.Name];
UserList = new List<SelectOption>();
UserList.add(new SelectOption('--Select--','--Select--'));
for(User temp : UserTemp)
{
UserList.add(new SelectOption(temp.Id, temp.Name));
}
return UserList;
}
set;
}
In Test Class
I am calling like this :
List<SelectOption> temp1 = TaskReportingMasterDataIssueController.UserList;
Please do respond !!!!!
Well, as I read your code, I understand you should make multiple test cases and in each one of them provide the corresponding values to issueTeam and query records so that all of the code is executed.
In the test line you have provided you are not including any context so I assume you're missing it in your tests.

Test class for trigger

I just wrote this trigger and it seems to be working fine in dev, I need to move it into production, however, the test class I wrote passes the test but does not cover the trigger. Any help would be greatly appreciated. I am a bit green here. I know I should be inserting a contact (the account is a req field) then updating the contact field I just have no earthly clue how to do taht. Thank you
trigger PropOwned on Contact (after update) {
for (Contact c : Trigger.new) {
McLabs2__Ownership__c ownNew = new McLabs2__Ownership__c();
Contact oldContact = Trigger.oldMap.get(c.id);
if (c.One_Prop_Owned__c != oldContact.One_Prop_Owned__c && c.One_Prop_Owned__c != null) {
ownNew.McLabs2__Contact__c = c.id;
ownNew.McLabs2__Property__c = c.One_Prop_Owned__c;
insert ownNew;
}
}
}
This is the test class I wrote.
#isTest
public class TestOwnership {
static testMethod void createOwnership() {
McLabs2__Ownership__c ownNew = new McLabs2__Ownership__c();
ownNew.McLabs2__Contact__c = 'Michael Webb';
ownNew.McLabs2__Property__c = '131 West 33rd Street';
insert ownNew;
}
}
Your test class just creates a McLabs2__Ownership__c object and inserts this object in database. As a result of this trigger on McLabs2__Ownership__c(if exist) will be invoked, but you have to test a trigger on Contact object. Thus you need to insert an account and after that update it because your contact trigger works in after update mode.
So, you need something like that
#isTest
private class TestOwnership {
static testMethod void whenContactUpdatedNewOwnershipIsInserted() {
// create contact, you have to replace 'value1' with appropriate data type
Contact contact = new Contact(name = 'Test Contact', One_Prop_Owned__c = 'value1');
insert contact;
contact.One_Prop_Owned__c = 'value2'; // you have to replace value2 with appropriate data type
update contact;
// in this place you should has only one record of McLabs2__Ownership__c in database, because in test context real data isn't visible
List<McLabs2__Ownership__c> ownerships = [SELECT Id, McLabs2__Contact__c, McLabs2__Property__c FROM McLabs2__Ownership__c];
System.assertEquals(1, ownerships.size());
System.assertEquals(contact.Id, ownerships[0].McLabs2__Contact__c);
System.assertEquals(contact.One_Prop_Owned__c, ownerships[0].McLabs2__Property__c);
}
}
Read the following articles which might be pretty useful for you:
Apex Trigger best practice
SOQL in loop

Error when updating many to many relation in EF5

I have an ASP.NET WebForms project with N-Layers using Entity Framework 5.
I have two entities: Cliente and Banda.
One Cliente may have many Banda's, and one Banda may have many Cliente's
In the bussines layer I have this code:
public void Update(Cliente cliente)
{
using (MegaStudioEntities contexto = new MegaStudioEntities())
{
if (contexto.Entry(cliente).State == EntityState.Detached)
contexto.Entry(cliente).State = EntityState.Modified;
//Delete existing relations
var qBandas = from qb in contexto.Bandas.Where(b => b.Clientes.Any(c => c.IdCliente == cliente.IdCliente))
select qb;
foreach (Banda b in qBandas.ToList())
((IObjectContextAdapter)contexto).ObjectContext.ObjectStateManager.ChangeRelationshipState(cliente, b, c => c.Bandas, EntityState.Deleted);
contexto.SaveChanges();
//Adding new relations
foreach (Banda banda in cliente.Bandas)
{
contexto.Bandas.Attach(banda);
((IObjectContextAdapter)contexto).ObjectContext.ObjectStateManager.ChangeRelationshipState(cliente, banda, c => c.Bandas, EntityState.Added);
}
cliente.TipoCliente = contexto.TipoClientes.Find(cliente.IdTipoCliente);
cliente.FechaModificacion = System.DateTime.Now;
Encriptar(cliente);
contexto.SaveChanges();
}
}
The first time I call Update method, run sucessfully, but the second time I get this error:
"An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key."
What I forget to close?
This is the correct way to update many to many relations in EF5?
Thanks in advance!!!
Martin
UPDATE 1:
Finally my code is like that:
public void Update(Cliente cliente)
{
using (MegaStudioEntities contexto = new MegaStudioEntities())
{
Cliente savedClient = contexto.Clientes.Find(cliente.IdCliente);
foreach (var banda in savedClient.Bandas.ToList())
{
savedClient.Bandas.Remove(contexto.Bandas.Find(banda.IdBanda));
}
foreach (var banda in cliente.Bandas)
{
savedClient.Bandas.Add(contexto.Bandas.Find(banda.IdBanda));
}
contexto.Entry(savedClient).CurrentValues.SetValues(cliente);
contexto.SaveChanges();
}
}
Thanks Gert Arnold!!!
You don't really have to attach any object to the context. So you can prevent this exception by not doing that.
public void Update(Cliente cliente)
{
using (MegaStudioEntities contexto = new MegaStudioEntities())
{
Cliente savedClient = contexto.TipoClientes.Find(cliente.IdCliente);
foreach (var banda in savedClient.Bandas.ToList())
{
savedClient.Bandas.Remove(banda);
}
foreach (var banda in cliente.Bandas)
{
savedClient.Bandas.Add(banda);
}
savedClient.IdTipoCliente = cliente.IdTipoCliente;
savedClient.FechaModificacion = System.DateTime.Now;
Encriptar(cliente);
contexto.SaveChanges();
}
}
I'm not sure if this break code in Encriptar(cliente); because (obviously) I don't know what happens there.
As you see, you add and remove associations in a m:m relationship by adding/removing objects. You hardly ever (probably never) need to manipulate relationship state explicitly. If you feel a need to do that it most likely indicates that you overlook an easier way to achieve what you want.

Entity Framework - Issue returning Relationship Entity

Ok, I must be working too hard because I can't get my head around what it takes to use the Entity Framework correctly.
Here is what I am trying to do:
I have two tables: HeaderTable and DetailTable. The DetailTable will have 1 to Many records for each row in HeaderTable. In my EDM I set up a Relationship between these two tables to reflect this.
Since there is now a relationship setup between these tables, I thought that by quering all the records in HeaderTable, I would be able to access the DetailTable collection created by the EDM (I can see the property when quering, but it's null).
Here is my query (this is a Silverlight app, so I am using the DomainContext on the client):
// myContext is instatiated with class scope
EntityQuery<Project> query = _myContext.GetHeadersQuery();
_myContext.Load<Project>(query);
Since these calls are asynchronous, I check the values after the callback has completed. When checking the value of _myContext.HeaderTable I have all the rows expected. However, the DetailsTable property within _myContext.HeaderTable is empty.
foreach (var h in _myContext.HeaderTable) // Has records
{
foreach (var d in h.DetailTable) // No records
{
string test = d.Description;
}
I'm assuming my query to return all HeaderTable objects needs to be modified to somehow return all the HeaderDetail collectoins for each HeaderTable row. I just don't understand how this non-logical modeling stuff works yet.
What am I doing wrong? Any help is greatly appriciated. If you need more information, just let me know. I will be happy to provide anything you need.
Thanks,
-Scott
What you're probably missing is the Include(), which I think is out of scope of the code you provided.
Check out this cool video; it explained everything about EDM and Linq-to-Entities to me:
http://msdn.microsoft.com/en-us/data/ff628210.aspx
In case you can't view video now, check out this piece of code I have based on those videos (sorry it's not in Silverlight, but it's the same basic idea, I hope).
The retrieval:
public List<Story> GetAllStories()
{
return context.Stories.Include("User").Include("StoryComments").Where(s => s.HostID == CurrentHost.ID).ToList();
}
Loading the the data:
private void LoadAllStories()
{
lvwStories.DataSource = TEContext.GetAllStories();
lvwStories.DataBind();
}
Using the data:
protected void lvwStories_ItemDataBound(object sender, ListViewItemEventArgs e)
{
if (e.Item.ItemType == ListViewItemType.DataItem)
{
Story story = e.Item.DataItem as Story;
// blah blah blah....
hlStory.Text = story.Title;
hlStory.NavigateUrl = "StoryView.aspx?id=" + story.ID;
lblStoryCommentCount.Text = "(" + story.StoryComments.Count.ToString() + " comment" + (story.StoryComments.Count > 1 ? "s" : "") + ")";
lblStoryBody.Text = story.Body;
lblStoryUser.Text = story.User.Username;
lblStoryDTS.Text = story.AddedDTS.ToShortTimeString();
}
}